/* * libjingle * Copyright 2009 Google Inc. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright notice, * this list of conditions and the following disclaimer in the documentation * and/or other materials provided with the distribution. * 3. The name of the author may not be used to endorse or promote products * derived from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #ifndef TALK_SESSION_MEDIA_SRTPFILTER_H_ #define TALK_SESSION_MEDIA_SRTPFILTER_H_ #include #include #include #include #include "talk/media/base/cryptoparams.h" #include "webrtc/p2p/base/sessiondescription.h" #include "webrtc/base/basictypes.h" #include "webrtc/base/criticalsection.h" #include "webrtc/base/scoped_ptr.h" #include "webrtc/base/sigslotrepeater.h" #include "webrtc/base/sslstreamadapter.h" // Forward declaration to avoid pulling in libsrtp headers here struct srtp_event_data_t; struct srtp_ctx_t; struct srtp_policy_t; namespace cricket { // Key is 128 bits and salt is 112 bits == 30 bytes. B64 bloat => 40 bytes. extern const int SRTP_MASTER_KEY_BASE64_LEN; // Needed for DTLS-SRTP extern const int SRTP_MASTER_KEY_KEY_LEN; extern const int SRTP_MASTER_KEY_SALT_LEN; class SrtpSession; class SrtpStat; void EnableSrtpDebugging(); void ShutdownSrtp(); // Class to transform SRTP to/from RTP. // Initialize by calling SetSend with the local security params, then call // SetRecv once the remote security params are received. At that point // Protect/UnprotectRt(c)p can be called to encrypt/decrypt data. // TODO: Figure out concurrency policy for SrtpFilter. class SrtpFilter { public: enum Mode { PROTECT, UNPROTECT }; enum Error { ERROR_NONE, ERROR_FAIL, ERROR_AUTH, ERROR_REPLAY, }; SrtpFilter(); ~SrtpFilter(); // Whether the filter is active (i.e. crypto has been properly negotiated). bool IsActive() const; // Indicates which crypto algorithms and keys were contained in the offer. // offer_params should contain a list of available parameters to use, or none, // if crypto is not desired. This must be called before SetAnswer. bool SetOffer(const std::vector& offer_params, ContentSource source); // Same as SetAnwer. But multiple calls are allowed to SetProvisionalAnswer // after a call to SetOffer. bool SetProvisionalAnswer(const std::vector& answer_params, ContentSource source); // Indicates which crypto algorithms and keys were contained in the answer. // answer_params should contain the negotiated parameters, which may be none, // if crypto was not desired or could not be negotiated (and not required). // This must be called after SetOffer. If crypto negotiation completes // successfully, this will advance the filter to the active state. bool SetAnswer(const std::vector& answer_params, ContentSource source); // Just set up both sets of keys directly. // Used with DTLS-SRTP. bool SetRtpParams(int send_cs, const uint8_t* send_key, int send_key_len, int recv_cs, const uint8_t* recv_key, int recv_key_len); bool SetRtcpParams(int send_cs, const uint8_t* send_key, int send_key_len, int recv_cs, const uint8_t* recv_key, int recv_key_len); // Encrypts/signs an individual RTP/RTCP packet, in-place. // If an HMAC is used, this will increase the packet size. bool ProtectRtp(void* data, int in_len, int max_len, int* out_len); // Overloaded version, outputs packet index. bool ProtectRtp(void* data, int in_len, int max_len, int* out_len, int64_t* index); bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len); // Decrypts/verifies an invidiual RTP/RTCP packet. // If an HMAC is used, this will decrease the packet size. bool UnprotectRtp(void* data, int in_len, int* out_len); bool UnprotectRtcp(void* data, int in_len, int* out_len); // Returns rtp auth params from srtp context. bool GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len); // Update the silent threshold (in ms) for signaling errors. void set_signal_silent_time(uint32_t signal_silent_time_in_ms); bool ResetParams(); sigslot::repeater3 SignalSrtpError; protected: bool ExpectOffer(ContentSource source); bool StoreParams(const std::vector& params, ContentSource source); bool ExpectAnswer(ContentSource source); bool DoSetAnswer(const std::vector& answer_params, ContentSource source, bool final); void CreateSrtpSessions(); bool NegotiateParams(const std::vector& answer_params, CryptoParams* selected_params); bool ApplyParams(const CryptoParams& send_params, const CryptoParams& recv_params); static bool ParseKeyParams(const std::string& params, uint8_t* key, int len); private: enum State { ST_INIT, // SRTP filter unused. ST_SENTOFFER, // Offer with SRTP parameters sent. ST_RECEIVEDOFFER, // Offer with SRTP parameters received. ST_SENTPRANSWER_NO_CRYPTO, // Sent provisional answer without crypto. // Received provisional answer without crypto. ST_RECEIVEDPRANSWER_NO_CRYPTO, ST_ACTIVE, // Offer and answer set. // SRTP filter is active but new parameters are offered. // When the answer is set, the state transitions to ST_ACTIVE or ST_INIT. ST_SENTUPDATEDOFFER, // SRTP filter is active but new parameters are received. // When the answer is set, the state transitions back to ST_ACTIVE. ST_RECEIVEDUPDATEDOFFER, // SRTP filter is active but the sent answer is only provisional. // When the final answer is set, the state transitions to ST_ACTIVE or // ST_INIT. ST_SENTPRANSWER, // SRTP filter is active but the received answer is only provisional. // When the final answer is set, the state transitions to ST_ACTIVE or // ST_INIT. ST_RECEIVEDPRANSWER }; State state_; uint32_t signal_silent_time_in_ms_; std::vector offer_params_; rtc::scoped_ptr send_session_; rtc::scoped_ptr recv_session_; rtc::scoped_ptr send_rtcp_session_; rtc::scoped_ptr recv_rtcp_session_; CryptoParams applied_send_params_; CryptoParams applied_recv_params_; }; // Class that wraps a libSRTP session. class SrtpSession { public: SrtpSession(); ~SrtpSession(); // Configures the session for sending data using the specified // cipher-suite and key. Receiving must be done by a separate session. bool SetSend(int cs, const uint8_t* key, int len); // Configures the session for receiving data using the specified // cipher-suite and key. Sending must be done by a separate session. bool SetRecv(int cs, const uint8_t* key, int len); // Encrypts/signs an individual RTP/RTCP packet, in-place. // If an HMAC is used, this will increase the packet size. bool ProtectRtp(void* data, int in_len, int max_len, int* out_len); // Overloaded version, outputs packet index. bool ProtectRtp(void* data, int in_len, int max_len, int* out_len, int64_t* index); bool ProtectRtcp(void* data, int in_len, int max_len, int* out_len); // Decrypts/verifies an invidiual RTP/RTCP packet. // If an HMAC is used, this will decrease the packet size. bool UnprotectRtp(void* data, int in_len, int* out_len); bool UnprotectRtcp(void* data, int in_len, int* out_len); // Helper method to get authentication params. bool GetRtpAuthParams(uint8_t** key, int* key_len, int* tag_len); // Update the silent threshold (in ms) for signaling errors. void set_signal_silent_time(uint32_t signal_silent_time_in_ms); // Calls srtp_shutdown if it's initialized. static void Terminate(); sigslot::repeater3 SignalSrtpError; private: bool SetKey(int type, int cs, const uint8_t* key, int len); // Returns send stream current packet index from srtp db. bool GetSendStreamPacketIndex(void* data, int in_len, int64_t* index); static bool Init(); void HandleEvent(const srtp_event_data_t* ev); static void HandleEventThunk(srtp_event_data_t* ev); static std::list* sessions(); srtp_ctx_t* session_; int rtp_auth_tag_len_; int rtcp_auth_tag_len_; rtc::scoped_ptr srtp_stat_; static bool inited_; static rtc::GlobalLockPod lock_; int last_send_seq_num_; RTC_DISALLOW_COPY_AND_ASSIGN(SrtpSession); }; // Class that collects failures of SRTP. class SrtpStat { public: SrtpStat(); // Report RTP protection results to the handler. void AddProtectRtpResult(uint32_t ssrc, int result); // Report RTP unprotection results to the handler. void AddUnprotectRtpResult(uint32_t ssrc, int result); // Report RTCP protection results to the handler. void AddProtectRtcpResult(int result); // Report RTCP unprotection results to the handler. void AddUnprotectRtcpResult(int result); // Get silent time (in ms) for SRTP statistics handler. uint32_t signal_silent_time() const { return signal_silent_time_; } // Set silent time (in ms) for SRTP statistics handler. void set_signal_silent_time(uint32_t signal_silent_time) { signal_silent_time_ = signal_silent_time; } // Sigslot for reporting errors. sigslot::signal3 SignalSrtpError; private: // For each different ssrc and error, we collect statistics separately. struct FailureKey { FailureKey() : ssrc(0), mode(SrtpFilter::PROTECT), error(SrtpFilter::ERROR_NONE) { } FailureKey(uint32_t in_ssrc, SrtpFilter::Mode in_mode, SrtpFilter::Error in_error) : ssrc(in_ssrc), mode(in_mode), error(in_error) {} bool operator <(const FailureKey& key) const { return (ssrc < key.ssrc) || (ssrc == key.ssrc && mode < key.mode) || (ssrc == key.ssrc && mode == key.mode && error < key.error); } uint32_t ssrc; SrtpFilter::Mode mode; SrtpFilter::Error error; }; // For tracing conditions for signaling, currently we only use // last_signal_time. Wrap this as a struct so that later on, if we need any // other improvements, it will be easier. struct FailureStat { FailureStat() : last_signal_time(0) { } explicit FailureStat(uint32_t in_last_signal_time) : last_signal_time(in_last_signal_time) {} void Reset() { last_signal_time = 0; } uint32_t last_signal_time; }; // Inspect SRTP result and signal error if needed. void HandleSrtpResult(const FailureKey& key); std::map failures_; // Threshold in ms to silent the signaling errors. uint32_t signal_silent_time_; RTC_DISALLOW_COPY_AND_ASSIGN(SrtpStat); }; } // namespace cricket #endif // TALK_SESSION_MEDIA_SRTPFILTER_H_