📄 srtp.c
字号:
* * If RCC rate is 1, RCC mode 1 and 2 are functionally identical. * * @param rate RoC Carry rate (MUST NOT be zero) */void srtp_setrcc_rate (srtp_session_t *s, uint16_t rate){ assert (rate != 0); s->rtp_rcc = rate;}/** AES-CM for RTP (salt = 14 bytes + 2 nul bytes) */static intrtp_crypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t roc, uint16_t seq, const uint32_t *salt, uint8_t *data, size_t len){ /* Determines cryptographic counter (IV) */ uint32_t counter[4]; counter[0] = salt[0]; counter[1] = salt[1] ^ ssrc; counter[2] = salt[2] ^ htonl (roc); counter[3] = salt[3] ^ htonl (seq << 16); /* Encryption */ return ctr_crypt (hd, counter, data, len);}/** Determines SRTP Roll-Over-Counter (in host-byte order) */static uint32_tsrtp_compute_roc (const srtp_session_t *s, uint16_t seq){ uint32_t roc = s->rtp_roc; if (((seq - s->rtp_seq) & 0xffff) < 0x8000) { /* Sequence is ahead, good */ if (seq < s->rtp_seq) roc++; /* Sequence number wrap */ } else { /* Sequence is late, bad */ if (seq > s->rtp_seq) roc--; /* Wrap back */ } return roc;}/** Returns RTP sequence (in host-byte order) */static inline uint16_t rtp_seq (const uint8_t *buf){ return (buf[2] << 8) | buf[3];}/** Message Authentication and Integrity for RTP */static const uint8_t *rtp_digest (srtp_session_t *s, const uint8_t *data, size_t len, uint32_t roc){ const gcry_md_hd_t md = s->rtp.mac; gcry_md_reset (md); gcry_md_write (md, data, len); gcry_md_write (md, &(uint32_t){ htonl (roc) }, 4); return gcry_md_read (md, 0);}/** * Encrypts/decrypts a RTP packet and updates SRTP context * (CTR block cypher mode of operation has identical encryption and * decryption function). * * @param buf RTP packet to be en-/decrypted * @param len RTP packet length * * @return 0 on success, in case of error: * EINVAL malformatted RTP packet * EACCES replayed packet or out-of-window or sync lost */static int srtp_crypt (srtp_session_t *s, uint8_t *buf, size_t len){ assert (s != NULL); if ((len < 12) || ((buf[0] >> 6) != 2)) return EINVAL; /* Computes encryption offset */ uint16_t offset = 12; offset += (buf[0] & 0xf) * 4; // skips CSRC if (buf[0] & 0x10) { uint16_t extlen; offset += 4; if (len < offset) return EINVAL; memcpy (&extlen, buf + offset - 2, 2); offset += htons (extlen); // skips RTP extension header } if (len < offset) return EINVAL; /* Determines RTP 48-bits counter and SSRC */ uint16_t seq = rtp_seq (buf); uint32_t roc = srtp_compute_roc (s, seq), ssrc; memcpy (&ssrc, buf + 8, 4); /* Updates ROC and sequence (it's safe now) */ int16_t diff = seq - s->rtp_seq; if (diff > 0) { /* Sequence in the future, good */ s->rtp.window = s->rtp.window << diff; s->rtp.window |= 1; s->rtp_seq = seq, s->rtp_roc = roc; } else { /* Sequence in the past/present, bad */ diff = -diff; if ((diff >= 64) || ((s->rtp.window >> diff) & 1)) return EACCES; /* Replay attack */ s->rtp.window |= 1 << diff; } /* Encrypt/Decrypt */ if (s->flags & SRTP_UNENCRYPTED) return 0; if (rtp_crypt (s->rtp.cipher, ssrc, roc, seq, s->rtp.salt, buf + offset, len - offset)) return EINVAL; return 0;}/** * Turns a RTP packet into a SRTP packet: encrypt it, then computes * the authentication tag and appends it. * Note that you can encrypt packet in disorder. * * @param buf RTP packet to be encrypted/digested * @param lenp pointer to the RTP packet length on entry, * set to the SRTP length on exit (undefined on non-ENOSPC error) * @param bufsize size (bytes) of the packet buffer * * @return 0 on success, in case of error: * EINVAL malformatted RTP packet or internal error * ENOSPC bufsize is too small to add authentication tag * (<lenp> will hold the required byte size) * EACCES packet would trigger a replay error on receiver */intsrtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize){ size_t len = *lenp; size_t tag_len = s->tag_len; if (!(s->flags & SRTP_UNAUTHENTICATED)) { *lenp = len + tag_len; if (bufsize < (len + tag_len)) return ENOSPC; } int val = srtp_crypt (s, buf, len); if (val) return val; if (!(s->flags & SRTP_UNAUTHENTICATED)) { uint32_t roc = srtp_compute_roc (s, rtp_seq (buf)); const uint8_t *tag = rtp_digest (s, buf, len, roc); if (rcc_mode (s)) { assert (s->rtp_rcc); if ((rtp_seq (buf) % s->rtp_rcc) == 0) { memcpy (buf + len, &(uint32_t){ htonl (s->rtp_roc) }, 4); len += 4; if (rcc_mode (s) == 3) tag_len = 0; else tag_len -= 4; } else { if (rcc_mode (s) & 1) tag_len = 0; } } memcpy (buf + len, tag, tag_len); } return 0;}/** * Turns a SRTP packet into a RTP packet: authenticates the packet, * then decrypts it. * * @param buf RTP packet to be digested/decrypted * @param lenp pointer to the SRTP packet length on entry, * set to the RTP length on exit (undefined in case of error) * * @return 0 on success, in case of error: * EINVAL malformatted SRTP packet * EACCES authentication failed (spoofed packet or out-of-sync) */intsrtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp){ size_t len = *lenp; if (len < 12u) return EINVAL; if (!(s->flags & SRTP_UNAUTHENTICATED)) { size_t tag_len = s->tag_len, roc_len = 0; if (rcc_mode (s)) { if ((rtp_seq (buf) % s->rtp_rcc) == 0) { roc_len = 4; if (rcc_mode (s) == 3) tag_len = 0; else tag_len -= 4; } else { if (rcc_mode (s) & 1) tag_len = 0; // RCC mode 1 or 3: no auth } } if (len < (12u + roc_len + tag_len)) return EINVAL; len -= roc_len + tag_len; uint32_t roc = srtp_compute_roc (s, rtp_seq (buf)), rcc; if (roc_len) { assert (roc_len == 4); memcpy (&rcc, buf + len, 4); rcc = ntohl (rcc); } else rcc = roc; const uint8_t *tag = rtp_digest (s, buf, len, rcc);#if 0 printf ("Computed: 0x"); for (unsigned i = 0; i < tag_len; i++) printf ("%02x", tag[i]); printf ("\nReceived: 0x"); for (unsigned i = 0; i < tag_len; i++) printf ("%02x", buf[len + roc_len + i]); puts ("");#endif if (memcmp (buf + len + roc_len, tag, tag_len)) return EACCES; if (roc_len) { /* Authenticated packet carried a Roll-Over-Counter */ s->rtp_roc += rcc - roc; assert (srtp_compute_roc (s, rtp_seq (buf)) == rcc); } *lenp = len; } return srtp_crypt (s, buf, len);}/** AES-CM for RTCP (salt = 14 bytes + 2 nul bytes) */static intrtcp_crypt (gcry_cipher_hd_t hd, uint32_t ssrc, uint32_t index, const uint32_t *salt, uint8_t *data, size_t len){ return rtp_crypt (hd, ssrc, index >> 16, index & 0xffff, salt, data, len);}/** Message Authentication and Integrity for RTCP */static const uint8_t *rtcp_digest (gcry_md_hd_t md, const void *data, size_t len){ gcry_md_reset (md); gcry_md_write (md, data, len); return gcry_md_read (md, 0);}/** * Encrypts/decrypts a RTCP packet and updates SRTCP context * (CTR block cypher mode of operation has identical encryption and * decryption function). * * @param buf RTCP packet to be en-/decrypted * @param len RTCP packet length * * @return 0 on success, in case of error: * EINVAL malformatted RTCP packet */static int srtcp_crypt (srtp_session_t *s, uint8_t *buf, size_t len){ assert (s != NULL); /* 8-bytes unencrypted header, and 4-bytes unencrypted footer */ if ((len < 12) || ((buf[0] >> 6) != 2)) return EINVAL; uint32_t index; memcpy (&index, buf + len, 4); index = ntohl (index); if (((index >> 31) != 0) != ((s->flags & SRTCP_UNENCRYPTED) == 0)) return EINVAL; // E-bit mismatch index &= ~(1 << 31); // clear E-bit for counter /* Updates SRTCP index (safe here) */ int32_t diff = index - s->rtcp_index; if (diff > 0) { /* Packet in the future, good */ s->rtcp.window = s->rtcp.window << diff; s->rtcp.window |= 1; s->rtcp_index = index; } else { /* Packet in the past/present, bad */ diff = -diff; if ((diff >= 64) || ((s->rtcp.window >> diff) & 1)) return EACCES; // replay attack! s->rtp.window |= 1 << diff; } /* Crypts SRTCP */ if (s->flags & SRTCP_UNENCRYPTED) return 0; uint32_t ssrc; memcpy (&ssrc, buf + 4, 4); if (rtcp_crypt (s->rtcp.cipher, ssrc, index, s->rtp.salt, buf + 8, len - 8)) return EINVAL; return 0;}/** * Turns a RTCP packet into a SRTCP packet: encrypt it, then computes * the authentication tag and appends it. * * @param buf RTCP packet to be encrypted/digested * @param lenp pointer to the RTCP packet length on entry, * set to the SRTCP length on exit (undefined in case of error) * @param bufsize size (bytes) of the packet buffer * * @return 0 on success, in case of error: * EINVAL malformatted RTCP packet or internal error * ENOSPC bufsize is too small (to add index and authentication tag) */intsrtcp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize){ size_t len = *lenp; if (bufsize < (len + 4 + s->tag_len)) return ENOSPC; uint32_t index = ++s->rtcp_index; if (index >> 31) s->rtcp_index = index = 0; /* 31-bit wrap */ if ((s->flags & SRTCP_UNENCRYPTED) == 0) index |= 0x80000000; /* Set Encrypted bit */ memcpy (buf + len, &(uint32_t){ htonl (index) }, 4); int val = srtcp_crypt (s, buf, len); if (val) return val; len += 4; /* Digests SRTCP index too */ const uint8_t *tag = rtcp_digest (s->rtp.mac, buf, len); memcpy (buf + len, tag, s->tag_len); *lenp = len + s->tag_len; return 0;}/** * Turns a SRTCP packet into a RTCP packet: authenticates the packet, * then decrypts it. * * @param buf RTCP packet to be digested/decrypted * @param lenp pointer to the SRTCP packet length on entry, * set to the RTCP length on exit (undefined in case of error) * * @return 0 on success, in case of error: * EINVAL malformatted SRTCP packet * EACCES authentication failed (spoofed packet or out-of-sync) */intsrtcp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp){ size_t len = *lenp; if (len < (4u + s->tag_len)) return EINVAL; len -= s->tag_len; const uint8_t *tag = rtcp_digest (s->rtp.mac, buf, len); if (memcmp (buf + len, tag, s->tag_len)) return EACCES; len -= 4; /* Remove SRTCP index before decryption */ *lenp = len; return srtp_crypt (s, buf, len);}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -