-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Don't reuse same SSRC (err_status_replay_old) #1
Comments
Let keep the whole conversation here: I create two srtp_t sessions, set policy.ssrc.type = ssrc_any_outbound in both of them, and then call to srtp_protect() to both of them by passing the same RTP packet/data with the same SSRC value in both calls (note that before calling srtp_protect() I do a memcpy of the original RTP data, so the original one remains unchanged when the second call to srtp_protect() takes place in the second session). In this scenario I get lot of err_status_replay_old errors ("replay check failed (index too old)"). So I wonder: may be libsrtp handles a global/static list of streams so two different and independent srtp_t sessions should NEVER try to protect the same RTP packet? If this is true, please confirm it. Thanks a lot. Collaborator Sorry for the delayed response. I was on travel last week. Are you using the same srtp_ctx_t* when invoking srtp_protect() for each packet? If so, it's probably reusing the same stream context for both packets since it uses the SSRC to lookup the stream context. The replay database is under the stream context. ibc commented on 22 Sep 2014 Thanks for the response. I think I know what is happening: The server (in which I use libsrtp) receives RTP from peer-A, unprotects it, sets its SSRC value to a fixed "1234" and protects it using the srtp_ctx_t* of peer-B.
Collaborator If you are using two srtp_ctx_t* references, one for peer A and one for peer B, then the err_status_replay_old error is likely due to a repeating sequence number on the context for peer B. Changing the SSRC will probably not make a difference. You'll probably have to maintain a separate RTP sequence number for peer B to make sure it's not reused after the peer A session is restarted. If you're not rekeying the context with peer B, then you shouldn't reuse that context when sending repeated packets. You'll probably want to rekey the peer B context. ibc commented on 22 Sep 2014 Thanks for your response. However I do not fully understand. I expect that, when calling srtp_protect(packet): First the SSRC of the packet is inspected within the srtp_ctx_t session. ibc commented on 22 Sep 2014 My server is a conference server that does not perform media mixing. Then Peer A sends again RTP (note that this RTP has nothing to do with the previous one as the client has been restarted). The server performs the same steps as above, and then sometimes I get the err_status_replay_old when calling srtp_protect() on the outbound srtp_ctx_t of Peer B and Peer C. So in this case, I understand that, indeed, the sequence numbers of those new RTP packets form Peer A may create a conflict with the previous RTP stream from Peer A, and hence the encrypt error. Am I right? If so, I understand that when Peer A is restarted and performs again the DTLS-SRTP setup, the server must choose a different SSRC value for the RTP coming from Peer A so the outbound srtp_ctx_t of Peer B and Peer C will see those packets belonging to a new "stream" so there won't be sequence number problems. Am I right? Thanks a lot. Collaborator Q: So in this case, I understand that, indeed, the sequence numbers of those new RTP packets form Peer A may create a conflict with the previous RTP stream from Peer A, and hence the encrypt error. Am I right? A: I believe you're correct. Peer A is likely repeating the sequence number of some packets, causing the error when you re-encrypt it for peer B (or C). Q: If so, I understand that when Peer A is restarted and performs again the DTLS-SRTP setup, the server must choose a different SSRC value for the RTP coming from Peer A so the outbound srtp_ctx_t of Peer B and Peer C will see those packets belonging to a new "stream" so there won't be sequence number problems. Am I right? A: Yes, choosing a new SSRC may resolve the issue. But it's highly recommended to rekey the session between the server and peer B (and peer C). Another approach may be to use the EKT feature in libsrtp. Unfortunately the EKT draft was never ratified. But EKT support was added to libsrtp at some point in the past. This is beyond my expertise since the EKT code was contributed by another developer. But my understanding is EKT was developed specifically to solve the problem you're addressing. You may want to take a look at the EKT draft. ibc commented on 22 Sep 2014 Thanks a lot! I will take a look to EKT, but first I want the "simple" case to properly work (and EKT may not be suitable for all the use-cases in my server given that in some cases it will behave as a gateway between DTLS-SRTP peers and SDES-SRTP or plain RTP peers. Anyhow, you replied: Yes, choosing a new SSRC may resolve the issue. But it's highly recommended to rekey the session between the server and peer B (and peer C) Instead of setting policy.ssrc.type = ssrc_any_outbound I may call to srtp_add_stream() when a new peer joins the "conference" and srtp_remove_stream() when the peer leaves it. This would work even if I set the SSRC from Peer A to the same value when it reconnects since at the time it has been disconnected I've removed its stream (SSRC) from the sessions of both Peer B and Peer C. Does it sound ok? Collaborator That should resolve your problem since invoking srtp_add_stream() will result in a new initialization of the anti-replay database for the stream. The streams are indexed by SSRC and normally created implicitly when using ssrc_any_outbound. You'll simply be creating the stream explicitly. But you really should rekey the other SRTP sessions to peers B & C. Otherwise you'll end up reusing the initialization vector for AES encryption, which is fatal when GCM mode is used. Collaborator Yes. It is a different source, in fact. ALternatively, make sure the peer-B context doesn't see the source as having stopped, and modify any after-reattachment seqnums and timestamps to make the interrupted source streams appear as a single stream with an interruption. ibc commented on 23 Sep 2014 But you really should rekey the other SRTP sessions to peers B & C. Otherwise you'll end up reusing the initialization vector for AES encryption, which is fatal when GCM mode is used. So, is it enough if I set a new SSRC for the incoming RTP of Peer A after it reconnects to the conference? ALternatively, make sure the peer-B context doesn't see the source as having stopped, and modify any after-reattachment seqnums and timestamps to make the interrupted source streams appear as a single stream with an interruption. Thanks a lot to both. Collaborator Yes, you would have to perform a new SRTP key exchange with both peers (B&C). I'm assuming you're using AES-CTR mode. The problem is CTR mode is a stream cipher. Any nonce reuse is considered catastrophic. Since the nonce is derived from the sequence number in the RTP packet, and you're potentially reusing sequence numbers (indicated by the error you received that started this discussion), then you're at risk for nonce reuse. The same problem is presented when using AES-GCM mode. This needs to be avoided if you wish to provide a secure solution. Take a look at EKT. It was designed with intent to solve the problem you've presented. ibc commented on 24 Sep 2014 Thanks a lot, but I would like to confirm that this problem: Since the nonce is derived from the sequence number in the RTP packet, and you're potentially reusing sequence numbers (indicated by the error you received that started this discussion), then you're at risk for nonce reuse BTW: How to select AES-CTR, AES-GCM or any other mode? I don't see that in the API. Is it something that I can set when creating a srtp session or policy? or does it depend on the peer? Regarding EKT, AFAIK it just make sense when both peers use SRTP, which may not be true in my case. Even more, I'm building a conference server (which does not mix but relays all the streams to other participants). Collaborator The SSRC is also a component into the nonce (a.k.a. IV) used for AES-CTR mode. If you're changing the SSRC, then this may help prevent nonce resuse. Bottom line, you'll need to ensure the nonce is never reused. Please refer to section 4.1.1 of RFC 3711 which warns about this and discusses the need to rekey. You'll need to understand how the nonce is derived and ensure it's never reused. Your approach of cloning the packing and hacking the SSRC may circumvent the nonce reuse checks within libsrtp. You would select the AES mode on the SRTP policy. The cipher_type member on the policy can be set to AES_ICM, AES_192_ICM, AES_256_ICM, AES_128_GCM, or AES_256_GCM. There are some other settings for this defined in crypto_types.h, but I've never seen them used and not sure if they are supported (SEAL, AES_CBC). ibc commented on 24 Sep 2014 Really thanks a lot. I just use crypto_policy_set_aes_cm_128_hmac_sha1_80() and crypto_policy_set_aes_cm_128_hmac_sha1_32() (depending on the SRTP suite negotiated via DTLS). Definitely I need to go in depth with RFC 3711. Will do it. I strongly appreciate all the help provided in this thread. I will close the issue given that it is not an issue :) Collaborator Using those policy helper functions, you're using AES-CTR mode. If you want GCM mode, you can use one of the GCM policy helpers (e.g. crypto_policy_set_aes_gcm_128_8_auth). GCM mode is only supported when the --enable-openssl option is used to configure libsrtp. |
TODO: Suggest in libsrtp project that streams/ssrcs are not globally stored by per SRTP session. /cc @jmillan |
UPDATE: I was wrong: SSRC (and hence |
So closing this issue. |
Info here: cisco/libsrtp#68 (comment)
The text was updated successfully, but these errors were encountered: