Skip to content

Commit

Permalink
[API] Add GCM to the SRT API.
Browse files Browse the repository at this point in the history
  • Loading branch information
maxsharabayko committed Oct 26, 2022
1 parent fda7441 commit 3e4561e
Show file tree
Hide file tree
Showing 10 changed files with 111 additions and 17 deletions.
2 changes: 1 addition & 1 deletion haicrypt/hcrypt_ctx_rx.c
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ int hcryptCtx_Rx_ParseKM(hcrypt_Session *crypto, unsigned char *km_msg, size_t m
return(-1);
}

if (HCRYPT_CIPHER_AES_CTR != km_msg[HCRYPT_MSG_KM_OFS_CIPHER]
if (HCRYPT_CIPHER_AES_CTR == km_msg[HCRYPT_MSG_KM_OFS_CIPHER]
&& HCRYPT_AUTH_NONE != km_msg[HCRYPT_MSG_KM_OFS_AUTH]) {
HCRYPT_LOG(LOG_WARNING, "%s", "KMmsg unsupported auth method\n");
return(-1);
Expand Down
2 changes: 1 addition & 1 deletion srtcore/buffer_rcv.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ int CRcvBuffer::dropMessage(int32_t seqnolo, int32_t seqnohi, int32_t msgno)
IF_RCVBUF_DEBUG(scoped_log.ss << "CRcvBuffer::dropMessage: seqnolo " << seqnolo << " seqnohi " << seqnohi << " m_iStartSeqNo " << m_iStartSeqNo);
// TODO: count bytes as removed?
const int end_pos = incPos(m_iStartPos, m_iMaxPosInc);
if (msgno != 0)
if (msgno > 0) // including SRT_MSGNO_NONE and SRT_MSGNO_CONTROL
{
IF_RCVBUF_DEBUG(scoped_log.ss << " msgno " << msgno);
int minDroppedOffset = -1;
Expand Down
2 changes: 1 addition & 1 deletion srtcore/buffer_rcv.h
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,7 @@ class CRcvBuffer
int dropAll();

/// @brief Drop the whole message from the buffer.
/// If message number is 0, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi].
/// If message number is 0 or SRT_MSGNO_NONE, then use sequence numbers to locate sequence range to drop [seqnolo, seqnohi].
/// When one packet of the message is in the range of dropping, the whole message is to be dropped.
/// @param seqnolo sequence number of the first packet in the dropping range.
/// @param seqnohi sequence number of the last packet in the dropping range.
Expand Down
48 changes: 44 additions & 4 deletions srtcore/core.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -203,6 +203,7 @@ struct SrtOptionAction
#endif
flags[SRTO_PACKETFILTER] = SRTO_R_PRE;
flags[SRTO_RETRANSMITALGO] = SRTO_R_PRE;
flags[SRTO_CRYPTOMODE] = SRTO_R_PRE;

// For "private" options (not derived from the listener
// socket by an accepted socket) provide below private_default
Expand Down Expand Up @@ -811,6 +812,11 @@ void srt::CUDT::getOpt(SRT_SOCKOPT optName, void *optval, int &optlen)
optlen = sizeof(int32_t);
break;

case SRTO_CRYPTOMODE:
*(int32_t*)optval = m_config.iCryptoMode;
optlen = sizeof(int32_t);
break;

default:
throw CUDTException(MJ_NOTSUP, MN_NONE, 0);
}
Expand Down Expand Up @@ -2629,6 +2635,13 @@ bool srt::CUDT::interpretSrtHandshake(const CHandShake& hs,
}
if (*pw_len == 1)
{
if (m_pCryptoControl->m_RcvKmState == SRT_KM_S_BADCRYPTOMODE)
{
// Cryptographic modes mismatch. Not acceptable at all.
m_RejectReason = SRT_REJ_CRYPTO;
return false;
}

// This means that there was an abnormal encryption situation occurred.
// This is inacceptable in case of strict encryption.
if (m_config.bEnforcedEnc)
Expand Down Expand Up @@ -9086,6 +9099,13 @@ int srt::CUDT::packLostData(CPacket& w_packet)
else if (payload == 0)
continue;

// The packet has been ecrypted, thus the authentication tag is expected to be stored
// in the SND buffer as well right after the payload.
if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)
{
w_packet.setLength(w_packet.getLength() + HAICRYPT_AUTHTAG_MAX);
}

// At this point we no longer need the ACK lock,
// because we are going to return from the function.
// Therefore unlocking in order not to block other threads.
Expand Down Expand Up @@ -9694,12 +9714,13 @@ int srt::CUDT::processData(CUnit* in_unit)
}

const int pktrexmitflag = m_bPeerRexmitFlag ? (packet.getRexmitFlag() ? 1 : 0) : 2;
const bool retransmitted = pktrexmitflag == 1;
#if ENABLE_HEAVY_LOGGING
static const char *const rexmitstat[] = {"ORIGINAL", "REXMITTED", "RXS-UNKNOWN"};
string rexmit_reason;
#endif

if (pktrexmitflag == 1)
if (retransmitted)
{
// This packet was retransmitted
enterCS(m_StatsLock);
Expand Down Expand Up @@ -9756,7 +9777,6 @@ int srt::CUDT::processData(CUnit* in_unit)
// this function will extract and test as needed.

const bool unordered = CSeqNo::seqcmp(packet.m_iSeqNo, m_iRcvCurrSeqNo) <= 0;
const bool retransmitted = m_bPeerRexmitFlag && packet.getRexmitFlag();

// Retransmitted and unordered packets do not provide expected measurement.
// We expect the 16th and 17th packet to be sent regularly,
Expand Down Expand Up @@ -9876,7 +9896,7 @@ int srt::CUDT::processData(CUnit* in_unit)
// Loop over all incoming packets that were filtered out.
// In case when there is no filter, there's just one packet in 'incoming',
// the one that came in the input of this function.
for (vector<CUnit *>::iterator unitIt = incoming.begin(); unitIt != incoming.end(); ++unitIt)
for (vector<CUnit *>::iterator unitIt = incoming.begin(); unitIt != incoming.end() && !m_bBroken; ++unitIt)
{
CUnit * u = *unitIt;
CPacket &rpkt = u->m_Packet;
Expand Down Expand Up @@ -9961,14 +9981,34 @@ int srt::CUDT::processData(CUnit* in_unit)
excessive = false;
if (u->m_Packet.getMsgCryptoFlags() != EK_NOENC)
{
EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP;
// TODO: reset and restore the timestamp if TSBPD is disabled.
// Reset retransmission flag (must be excluded from GCM auth tag).
u->m_Packet.setRexmitFlag(false);
const EncryptionStatus rc = m_pCryptoControl ? m_pCryptoControl->decrypt((u->m_Packet)) : ENCS_NOTSUP;
u->m_Packet.setRexmitFlag(retransmitted); // Recover the flag.

if (rc != ENCS_CLEAR)
{
// Heavy log message because if seen once the message may happen very often.
HLOGC(qrlog.Debug, log << CONID() << "ERROR: packet not decrypted, dropping data.");
adding_successful = false;
IF_HEAVY_LOGGING(exc_type = "UNDECRYPTED");

if (m_config.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM)
{
// Drop a packet from the receiver buffer.
// Dropping depends on the configuration mode. If message mode is enabled, we have to drop the whole message.
// Otherwise just drop the exact packet.
if (m_config.bMessageAPI)
m_pRcvBuffer->dropMessage(SRT_SEQNO_NONE, SRT_SEQNO_NONE, u->m_Packet.getMsgSeq(m_bPeerRexmitFlag));
else
m_pRcvBuffer->dropMessage(u->m_Packet.getSeqNo(), u->m_Packet.getSeqNo(), SRT_MSGNO_NONE);

LOGC(qrlog.Error, log << CONID() << "AEAD decryption failed, breaking the connection.");
m_bBroken = true;
m_iBrokenCounter = 0;
}

ScopedLock lg(m_StatsLock);
m_stats.rcvr.undecrypted.count(stats::BytesPackets(pktsz, 1));
}
Expand Down
20 changes: 18 additions & 2 deletions srtcore/crypto.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -240,7 +240,7 @@ int srt::CCryptoControl::processSrtMsg_KMREQ(
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADSECRET");
break;
case HAICRYPT_ERROR_CIPHER:
m_RcvKmState = m_SndKmState = SRT_KM_S_BADSECRET; // TODO: Use a different, dedicated state.
m_RcvKmState = m_SndKmState = SRT_KM_S_BADCRYPTOMODE;
w_srtlen = 1;
LOGC(cnlog.Warn, log << "KMREQ/rcv: (snd) Rx process failure - BADCRYPTOMODE");
break;
Expand Down Expand Up @@ -393,6 +393,13 @@ int srt::CCryptoControl::processSrtMsg_KMRSP(const uint32_t* srtdata, size_t len
retstatus = 0;
break;

case SRT_KM_S_BADCRYPTOMODE:
// The peer expects to use a different cryptographic mode (e.g. AES-GCM, not AES-CTR).
m_RcvKmState = SRT_KM_S_BADCRYPTOMODE;
m_SndKmState = SRT_KM_S_BADCRYPTOMODE;
retstatus = -1;
break;

default:
LOGC(cnlog.Fatal, log << "processSrtMsg_KMRSP: IPE: unknown peer error state: "
<< KmStateStr(peerstate) << " (" << int(peerstate) << ")");
Expand Down Expand Up @@ -601,6 +608,15 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b

// Set UNSECURED state as default
m_RcvKmState = SRT_KM_S_UNSECURED;
m_bUseGCM = cfg.iCryptoMode == CSrtConfig::CIPHER_MODE_AES_GCM;

#ifdef SRT_ENABLE_ENCRYPTION
if (m_bUseGCM && !isAESGCMSupported())
{
LOGC(cnlog.Warn, log << "CCryptoControl: AES GCM is not supported by the crypto service provider.");
return false;
}
#endif

// Set security-pending state, if a password was set.
m_SndKmState = hasPassphrase() ? SRT_KM_S_SECURING : SRT_KM_S_UNSECURED;
Expand Down Expand Up @@ -649,7 +665,7 @@ bool srt::CCryptoControl::init(HandshakeSide side, const CSrtConfig& cfg, bool b
// is turned off at compile time. Setting the password itself should be not allowed
// so this could only happen as a consequence of an IPE.
LOGC(cnlog.Error, log << "CCryptoControl::init: IPE: encryption not supported");
return true;
return false;
#endif
}
else
Expand Down
3 changes: 2 additions & 1 deletion srtcore/handshake.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,8 @@ const char* srt_rejectreason_name [] = {
"CONGESTION",
"FILTER",
"GROUP",
"TIMEOUT"
"TIMEOUT",
"CRYPTO"
};
}

Expand Down
23 changes: 23 additions & 0 deletions srtcore/socketconfig.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -883,6 +883,28 @@ struct CSrtConfigSetter<SRTO_RETRANSMITALGO>
}
};

template<>
struct CSrtConfigSetter<SRTO_CRYPTOMODE>
{
static void set(CSrtConfig& co, const void* optval, int optlen)
{
const int val = cast_optval<int>(optval, optlen);
if (val < CSrtConfig::CIPHER_MODE_AUTO || val > CSrtConfig::CIPHER_MODE_AES_GCM)
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);

#ifdef SRT_ENABLE_ENCRYPTION
if (val == CSrtConfig::CIPHER_MODE_AES_GCM && !HaiCrypt_IsAESGCM_Supported())
{
using namespace srt_logging;
LOGC(aclog.Error, log << "AES GCM is not supported by the crypto provider.");
throw CUDTException(MJ_NOTSUP, MN_INVAL, 0);
}
#endif

co.iCryptoMode = val;
}
};

int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int optlen)
{
switch (optName)
Expand Down Expand Up @@ -940,6 +962,7 @@ int dispatchSet(SRT_SOCKOPT optName, CSrtConfig& co, const void* optval, int opt
DISPATCH(SRTO_IPV6ONLY);
DISPATCH(SRTO_PACKETFILTER);
DISPATCH(SRTO_RETRANSMITALGO);
DISPATCH(SRTO_CRYPTOMODE);

#undef DISPATCH
default:
Expand Down
9 changes: 9 additions & 0 deletions srtcore/socketconfig.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,6 +176,13 @@ struct CSrtConfig: CSrtMuxerConfig
DEF_LINGER_S = 3*60, // 3 minutes
DEF_CONNTIMEO_S = 3; // 3 seconds

enum
{
CIPHER_MODE_AUTO = 0,
CIPHER_MODE_AES_CTR = 1,
CIPHER_MODE_AES_GCM = 2
};

static const int COMM_RESPONSE_TIMEOUT_MS = 5 * 1000; // 5 seconds
static const uint32_t COMM_DEF_MIN_STABILITY_TIMEOUT_MS = 60; // 60 ms

Expand Down Expand Up @@ -224,6 +231,7 @@ struct CSrtConfig: CSrtMuxerConfig
int iPeerIdleTimeout_ms; // Timeout for hearing anything from the peer (ms).
uint32_t uMinStabilityTimeout_ms;
int iRetransmitAlgo;
int iCryptoMode; // SRTO_CRYPTOMODE

int64_t llInputBW; // Input stream rate (bytes/sec). 0: use internally estimated input bandwidth
int64_t llMinInputBW; // Minimum input stream rate estimate (bytes/sec)
Expand Down Expand Up @@ -275,6 +283,7 @@ struct CSrtConfig: CSrtMuxerConfig
, iPeerIdleTimeout_ms(COMM_RESPONSE_TIMEOUT_MS)
, uMinStabilityTimeout_ms(COMM_DEF_MIN_STABILITY_TIMEOUT_MS)
, iRetransmitAlgo(1)
, iCryptoMode(CIPHER_MODE_AUTO)
, llInputBW(0)
, llMinInputBW(0)
, iOverheadBW(25)
Expand Down
13 changes: 8 additions & 5 deletions srtcore/srt.h
Original file line number Diff line number Diff line change
Expand Up @@ -239,6 +239,7 @@ typedef enum SRT_SOCKOPT {
SRTO_GROUPTYPE, // Group type to which an accepted socket is about to be added, available in the handshake (ENABLE_BONDING)
SRTO_PACKETFILTER = 60, // Add and configure a packet filter
SRTO_RETRANSMITALGO = 61, // An option to select packet retransmission algorithm
SRTO_CRYPTOMODE = 62, // Encryption cipher mode (AES-CTR, AES-GCM, ...).

SRTO_E_SIZE // Always last element, not a valid option.
} SRT_SOCKOPT;
Expand Down Expand Up @@ -553,6 +554,7 @@ enum SRT_REJECT_REASON
SRT_REJ_FILTER, // incompatible packet filter
SRT_REJ_GROUP, // incompatible group
SRT_REJ_TIMEOUT, // connection timeout
SRT_REJ_CRYPTO, // conflicting cryptographic configurations

SRT_REJ_E_SIZE,
};
Expand Down Expand Up @@ -634,11 +636,12 @@ enum SRT_REJECT_REASON

enum SRT_KM_STATE
{
SRT_KM_S_UNSECURED = 0, //No encryption
SRT_KM_S_SECURING = 1, //Stream encrypted, exchanging Keying Material
SRT_KM_S_SECURED = 2, //Stream encrypted, keying Material exchanged, decrypting ok.
SRT_KM_S_NOSECRET = 3, //Stream encrypted and no secret to decrypt Keying Material
SRT_KM_S_BADSECRET = 4 //Stream encrypted and wrong secret, cannot decrypt Keying Material
SRT_KM_S_UNSECURED = 0, // No encryption
SRT_KM_S_SECURING = 1, // Stream encrypted, exchanging Keying Material
SRT_KM_S_SECURED = 2, // Stream encrypted, keying Material exchanged, decrypting ok.
SRT_KM_S_NOSECRET = 3, // Stream encrypted and no secret to decrypt Keying Material
SRT_KM_S_BADSECRET = 4, // Stream encrypted and wrong secret is used, cannot decrypt Keying Material
SRT_KM_S_BADCRYPTOMODE = 5 // Stream encrypted but wrong ccryptographic mode is used, cannot decrypt. Since v1.6.0.
};

enum SRT_EPOLL_OPT
Expand Down
6 changes: 4 additions & 2 deletions srtcore/srt_c_api.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -438,7 +438,8 @@ const char* const srt_rejection_reason_msg [] = {
"Congestion controller type collision",
"Packet Filter settings error",
"Group settings collision",
"Connection timeout"
"Connection timeout",
"Crypto mode"
};

// Deprecated, available in SRT API.
Expand All @@ -459,7 +460,8 @@ extern const char* const srt_rejectreason_msg[] = {
srt_rejection_reason_msg[13],
srt_rejection_reason_msg[14],
srt_rejection_reason_msg[15],
srt_rejection_reason_msg[16]
srt_rejection_reason_msg[16],
srt_rejection_reason_msg[17]
};

const char* srt_rejectreason_str(int id)
Expand Down

0 comments on commit 3e4561e

Please sign in to comment.