123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283 |
- /*
- * SRT - Secure, Reliable, Transport
- * Copyright (c) 2018 Haivision Systems Inc.
- *
- * This Source Code Form is subject to the terms of the Mozilla Public
- * License, v. 2.0. If a copy of the MPL was not distributed with this
- * file, You can obtain one at http://mozilla.org/MPL/2.0/.
- *
- */
- /*****************************************************************************
- written by
- Haivision Systems Inc.
- *****************************************************************************/
- #ifndef INC_SRT_CRYPTO_H
- #define INC_SRT_CRYPTO_H
- #include <cstring>
- #include <string>
- // UDT
- #include "udt.h"
- #include "packet.h"
- #include "utilities.h"
- #include "logging.h"
- #include <haicrypt.h>
- #include <hcrypt_msg.h>
- namespace srt_logging
- {
- std::string KmStateStr(SRT_KM_STATE state);
- #if ENABLE_LOGGING
- extern Logger cnlog;
- #endif
- }
- namespace srt
- {
- class CUDT;
- struct CSrtConfig;
- // For KMREQ/KMRSP. Only one field is used.
- const size_t SRT_KMR_KMSTATE = 0;
- #define SRT_CMD_MAXSZ HCRYPT_MSG_KM_MAX_SZ /* Maximum SRT custom messages payload size (bytes) */
- const size_t SRTDATA_MAXSIZE = SRT_CMD_MAXSZ/sizeof(uint32_t);
- class CCryptoControl
- {
- SRTSOCKET m_SocketID;
- size_t m_iSndKmKeyLen; //Key length
- size_t m_iRcvKmKeyLen; //Key length from rx KM
- // Temporarily allow these to be accessed.
- public:
- SRT_KM_STATE m_SndKmState; //Sender Km State (imposed by agent)
- SRT_KM_STATE m_RcvKmState; //Receiver Km State (informed by peer)
- private:
- // Partial haicrypt configuration, consider
- // putting the whole HaiCrypt_Cfg object here.
- int m_KmRefreshRatePkt;
- int m_KmPreAnnouncePkt;
- int m_iCryptoMode;
- HaiCrypt_Secret m_KmSecret; //Key material shared secret
- // Sender
- sync::steady_clock::time_point m_SndKmLastTime;
- sync::Mutex m_mtxLock; // A mutex to protect concurrent access to CCryptoControl.
- struct {
- unsigned char Msg[HCRYPT_MSG_KM_MAX_SZ];
- size_t MsgLen;
- int iPeerRetry;
- } m_SndKmMsg[2];
- HaiCrypt_Handle m_hSndCrypto;
- // Receiver
- HaiCrypt_Handle m_hRcvCrypto;
- bool m_bErrorReported;
- public:
- static void globalInit();
- static bool isAESGCMSupported();
- bool sendingAllowed()
- {
- // This function is called to state as to whether the
- // crypter allows the packet to be sent over the link.
- // This is possible in two cases:
- // - when Agent didn't set a password, no matter the crypto state
- if (m_KmSecret.len == 0)
- return true;
- // - when Agent did set a password and the crypto state is SECURED.
- if (m_KmSecret.len > 0 && m_SndKmState == SRT_KM_S_SECURED
- // && m_iRcvPeerKmState == SRT_KM_S_SECURED ?
- )
- return true;
- return false;
- }
- bool hasPassphrase() const
- {
- return m_KmSecret.len > 0;
- }
- int getCryptoMode() const
- {
- return m_iCryptoMode;
- }
- /// Regenerate cryptographic key material if needed.
- /// @param[in] sock If not null, the socket will be used to send the KM message to the peer (e.g. KM refresh).
- /// @param[in] bidirectional If true, the key material will be regenerated for both directions (receiver and sender).
- SRT_ATTR_EXCLUDES(m_mtxLock)
- void regenCryptoKm(CUDT* sock, bool bidirectional);
- size_t KeyLen() { return m_iSndKmKeyLen; }
- // Needed for CUDT
- void updateKmState(int cmd, size_t srtlen);
- // Detailed processing
- int processSrtMsg_KMREQ(const uint32_t* srtdata, size_t len, int hsv,
- uint32_t srtdata_out[], size_t&);
- // This returns:
- // 1 - the given payload is the same as the currently used key
- // 0 - there's no key in agent or the payload is error message with agent NOSECRET.
- // -1 - the payload is error message with other state or it doesn't match the key
- int processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len, int hsv);
- void createFakeSndContext();
- const unsigned char* getKmMsg_data(size_t ki) const { return m_SndKmMsg[ki].Msg; }
- size_t getKmMsg_size(size_t ki) const { return m_SndKmMsg[ki].MsgLen; }
- /// Check if the key stored at @c ki shall be sent. When during the handshake,
- /// it only matters if the KM message for that index is recorded at all.
- /// Otherwise returns true only if also the retry counter didn't expire.
- ///
- /// @param ki Key index (0 or 1)
- /// @param runtime True, if this happens as a key update
- /// during transmission (otherwise it's during the handshake)
- /// @return Whether the KM message at given index needs to be sent.
- bool getKmMsg_needSend(size_t ki, bool runtime) const
- {
- if (runtime)
- return (m_SndKmMsg[ki].iPeerRetry > 0 && m_SndKmMsg[ki].MsgLen > 0);
- else
- return m_SndKmMsg[ki].MsgLen > 0;
- }
- /// Mark the key as already sent. When no 'runtime' (during the handshake)
- /// it actually does nothing so that this will be retried as long as the handshake
- /// itself is being retried. Otherwise this is during transmission and will expire
- /// after several retries.
- ///
- /// @param ki Key index (0 or 1)
- /// @param runtime True, if this happens as a key update
- /// during transmission (otherwise it's during the handshake)
- void getKmMsg_markSent(size_t ki, bool runtime)
- {
- #if ENABLE_LOGGING
- using srt_logging::cnlog;
- #endif
- m_SndKmLastTime = sync::steady_clock::now();
- if (runtime)
- {
- m_SndKmMsg[ki].iPeerRetry--;
- HLOGC(cnlog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " retry=" << m_SndKmMsg[ki].iPeerRetry);
- }
- else
- {
- HLOGC(cnlog.Debug, log << "getKmMsg_markSent: key[" << ki << "]: len=" << m_SndKmMsg[ki].MsgLen << " STILL IN USE.");
- }
- }
- /// Check if the response returned by KMRSP matches the recorded KM message.
- /// When it is, set also the retry counter to 0 to prevent further retries.
- ///
- /// @param ki KM message index (0 or 1)
- /// @param srtmsg Message received through KMRSP
- /// @param bytesize Size of the message
- /// @return True if the message is identical to the recorded KM message at given index.
- bool getKmMsg_acceptResponse(size_t ki, const uint32_t* srtmsg, size_t bytesize)
- {
- if ( m_SndKmMsg[ki].MsgLen == bytesize
- && 0 == memcmp(m_SndKmMsg[ki].Msg, srtmsg, m_SndKmMsg[ki].MsgLen))
- {
- m_SndKmMsg[ki].iPeerRetry = 0;
- return true;
- }
- return false;
- }
- CCryptoControl(SRTSOCKET id);
- // DEBUG PURPOSES:
- std::string CONID() const;
- std::string FormatKmMessage(std::string hdr, int cmd, size_t srtlen);
- bool init(HandshakeSide, const CSrtConfig&, bool);
- SRT_ATTR_EXCLUDES(m_mtxLock)
- void close();
- /// (Re)send KM request to a peer on timeout.
- /// This function is used in:
- /// - HSv4 (initial key material exchange - in HSv5 it's attached to handshake).
- /// - The case of key regeneration (KM refresh), when a new key has to be sent again.
- /// In this case the first sending happens in regenCryptoKm(..). This function
- /// retransmits the KM request by timeout if not KM response has been received.
- SRT_ATTR_EXCLUDES(m_mtxLock)
- void sendKeysToPeer(CUDT* sock, int iSRTT);
- void setCryptoSecret(const HaiCrypt_Secret& secret)
- {
- m_KmSecret = secret;
- }
- void setCryptoKeylen(size_t keylen)
- {
- m_iSndKmKeyLen = keylen;
- m_iRcvKmKeyLen = keylen;
- }
- bool createCryptoCtx(HaiCrypt_Handle& rh, size_t keylen, HaiCrypt_CryptoDir tx, bool bAESGCM);
- int getSndCryptoFlags() const
- {
- #ifdef SRT_ENABLE_ENCRYPTION
- return(m_hSndCrypto ?
- HaiCrypt_Tx_GetKeyFlags(m_hSndCrypto) :
- // When encryption isn't on, check if it was required
- // If it was, return -1 as flags, which means that
- // encryption was requested and not possible.
- hasPassphrase() ? -1 :
- 0);
- #else
- return 0;
- #endif
- }
- bool isSndEncryptionOK() const
- {
- // Similar to this above, just quickly check if the encryption
- // is required and possible, or not possible
- if (!hasPassphrase())
- return true; // no encryption required
- if (m_hSndCrypto)
- return true; // encryption is required and possible
- return false;
- }
- /// Encrypts the packet. If encryption is not turned on, it
- /// does nothing. If the encryption is not correctly configured,
- /// the encryption will fail.
- /// XXX Encryption flags in the PH_MSGNO
- /// field in the header must be correctly set before calling.
- EncryptionStatus encrypt(CPacket& w_packet);
- /// Decrypts the packet. If the packet has ENCKEYSPEC part
- /// in PH_MSGNO set to EK_NOENC, it does nothing. It decrypts
- /// only if the encryption correctly configured, otherwise it
- /// fails. After successful decryption, the ENCKEYSPEC part
- // in PH_MSGNO is set to EK_NOENC.
- EncryptionStatus decrypt(CPacket& w_packet);
- ~CCryptoControl();
- };
- } // namespace srt
- #endif // SRT_CONGESTION_CONTROL_H
|