From b393d4160182873ea2acd9fbc6421f7e1a3adb9e Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 23 Mar 2021 15:05:50 +0100 Subject: Add support for SRTP --- plugins/crypto-vala/CMakeLists.txt | 1 + plugins/crypto-vala/src/random.vala | 5 +++++ 2 files changed, 6 insertions(+) create mode 100644 plugins/crypto-vala/src/random.vala (limited to 'plugins/crypto-vala') diff --git a/plugins/crypto-vala/CMakeLists.txt b/plugins/crypto-vala/CMakeLists.txt index 2c9f790a..036e45f6 100644 --- a/plugins/crypto-vala/CMakeLists.txt +++ b/plugins/crypto-vala/CMakeLists.txt @@ -10,6 +10,7 @@ SOURCES "src/cipher.vala" "src/cipher_converter.vala" "src/error.vala" + "src/random.vala" CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gcrypt.vapi" PACKAGES diff --git a/plugins/crypto-vala/src/random.vala b/plugins/crypto-vala/src/random.vala new file mode 100644 index 00000000..3f5d3ba9 --- /dev/null +++ b/plugins/crypto-vala/src/random.vala @@ -0,0 +1,5 @@ +namespace Crypto { +public static void randomize(uint8[] buffer) { + GCrypt.Random.randomize(buffer); +} +} \ No newline at end of file -- cgit v1.2.3-70-g09d2 From 4b230808b9566322fae8d1ef0d1a5cb3e8027d3b Mon Sep 17 00:00:00 2001 From: Marvin W Date: Tue, 23 Mar 2021 20:04:28 +0100 Subject: Move SRTP implementation into crypto library for reuse --- plugins/crypto-vala/CMakeLists.txt | 27 +- plugins/crypto-vala/src/srtp.c | 836 +++++++++++++++++++++++++++++++++++++ plugins/crypto-vala/src/srtp.h | 82 ++++ plugins/crypto-vala/src/srtp.vapi | 107 +++++ plugins/rtp/CMakeLists.txt | 5 +- plugins/rtp/src/srtp.c | 836 ------------------------------------- plugins/rtp/src/srtp.h | 82 ---- plugins/rtp/src/srtp.vapi | 103 ----- plugins/rtp/src/stream.vala | 20 +- 9 files changed, 1063 insertions(+), 1035 deletions(-) create mode 100644 plugins/crypto-vala/src/srtp.c create mode 100644 plugins/crypto-vala/src/srtp.h create mode 100644 plugins/crypto-vala/src/srtp.vapi delete mode 100644 plugins/rtp/src/srtp.c delete mode 100644 plugins/rtp/src/srtp.h delete mode 100644 plugins/rtp/src/srtp.vapi (limited to 'plugins/crypto-vala') diff --git a/plugins/crypto-vala/CMakeLists.txt b/plugins/crypto-vala/CMakeLists.txt index 036e45f6..f97b0d31 100644 --- a/plugins/crypto-vala/CMakeLists.txt +++ b/plugins/crypto-vala/CMakeLists.txt @@ -11,6 +11,7 @@ SOURCES "src/cipher_converter.vala" "src/error.vala" "src/random.vala" + "src/srtp.vapi" CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gcrypt.vapi" PACKAGES @@ -21,9 +22,33 @@ GENERATE_HEADER crypto-vala ) +add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/srtp.h" +COMMAND + cp "${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.h" "${CMAKE_BINARY_DIR}/exports/srtp.h" +DEPENDS + "${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.h" +COMMENT + Copy header file srtp.h +) + +add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/exports/crypto.vapi +COMMAND + cat "${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi" "${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.vapi" > "${CMAKE_BINARY_DIR}/exports/crypto.vapi" +DEPENDS + ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi + ${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.vapi +) + +add_custom_target(crypto-vapi +DEPENDS + ${CMAKE_BINARY_DIR}/exports/crypto.vapi + ${CMAKE_BINARY_DIR}/exports/srtp.h +) + set(CFLAGS ${VALA_CFLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/src) add_definitions(${CFLAGS}) -add_library(crypto-vala STATIC ${CRYPTO_VALA_C}) +add_library(crypto-vala STATIC ${CRYPTO_VALA_C} src/srtp.c) +add_dependencies(crypto-vala crypto-vapi) target_link_libraries(crypto-vala ${CRYPTO_VALA_PACKAGES} gcrypt) set_property(TARGET crypto-vala PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/plugins/crypto-vala/src/srtp.c b/plugins/crypto-vala/src/srtp.c new file mode 100644 index 00000000..708244d9 --- /dev/null +++ b/plugins/crypto-vala/src/srtp.c @@ -0,0 +1,836 @@ +/* + * Secure RTP with libgcrypt + * Copyright (C) 2007 Rémi Denis-Courmont + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + */ + +/* TODO: + * Useless stuff (because nothing depends on it): + * - non-nul key derivation rate + * - MKI payload + */ + +#ifdef HAVE_CONFIG_H +# include +#endif + +#include +#include + +#include "srtp.h" + +#include +#include +#include +#include + +#include + +#ifdef _WIN32 +# include +#else +# include +#endif + +#define debug( ... ) (void)0 + +typedef struct srtp_proto_t +{ + gcry_cipher_hd_t cipher; + gcry_md_hd_t mac; + uint64_t window; + uint32_t salt[4]; +} srtp_proto_t; + +struct srtp_session_t +{ + srtp_proto_t rtp; + srtp_proto_t rtcp; + unsigned flags; + unsigned kdr; + uint32_t rtcp_index; + uint32_t rtp_roc; + uint16_t rtp_seq; + uint16_t rtp_rcc; + uint8_t tag_len; +}; + +enum +{ + SRTP_CRYPT, + SRTP_AUTH, + SRTP_SALT, + SRTCP_CRYPT, + SRTCP_AUTH, + SRTCP_SALT +}; + + +static inline unsigned rcc_mode (const srtp_session_t *s) +{ + return (s->flags >> 4) & 3; +} + + +static void proto_destroy (srtp_proto_t *p) +{ + gcry_md_close (p->mac); + gcry_cipher_close (p->cipher); +} + + +/** + * Releases all resources associated with a Secure RTP session. + */ +void srtp_destroy (srtp_session_t *s) +{ + assert (s != NULL); + + proto_destroy (&s->rtcp); + proto_destroy (&s->rtp); + free (s); +} + + +static int proto_create (srtp_proto_t *p, int gcipher, int gmd) +{ + if (gcry_cipher_open (&p->cipher, gcipher, GCRY_CIPHER_MODE_CTR, 0) == 0) + { + if (gcry_md_open (&p->mac, gmd, GCRY_MD_FLAG_HMAC) == 0) + return 0; + gcry_cipher_close (p->cipher); + } + return -1; +} + + +/** + * Allocates a Secure RTP one-way session. + * The same session cannot be used both ways because this would confuse + * internal cryptographic counters; it is however of course feasible to open + * multiple simultaneous sessions with the same master key. + * + * @param encr encryption algorithm number + * @param auth authentication algortihm number + * @param tag_len authentication tag byte length (NOT including RCC) + * @param flags OR'ed optional flags. + * + * @return NULL in case of error + */ +srtp_session_t * +srtp_create (int encr, int auth, unsigned tag_len, int prf, unsigned flags) +{ + if ((flags & ~SRTP_FLAGS_MASK)) + return NULL; + + int cipher, md; + switch (encr) + { + case SRTP_ENCR_NULL: + cipher = GCRY_CIPHER_NONE; + break; + + case SRTP_ENCR_AES_CM: + cipher = GCRY_CIPHER_AES; + break; + + default: + return NULL; + } + + switch (auth) + { + case SRTP_AUTH_NULL: + md = GCRY_MD_NONE; + break; + + case SRTP_AUTH_HMAC_SHA1: + md = GCRY_MD_SHA1; + break; + + default: + return NULL; + } + + if (tag_len > gcry_md_get_algo_dlen (md)) + return NULL; + + if (prf != SRTP_PRF_AES_CM) + return NULL; + + srtp_session_t *s = malloc (sizeof (*s)); + if (s == NULL) + return NULL; + + memset (s, 0, sizeof (*s)); + s->flags = flags; + s->tag_len = tag_len; + s->rtp_rcc = 1; /* Default RCC rate */ + if (rcc_mode (s)) + { + if (tag_len < 4) + goto error; + } + + if (proto_create (&s->rtp, cipher, md) == 0) + { + if (proto_create (&s->rtcp, cipher, md) == 0) + return s; + proto_destroy (&s->rtp); + } + + error: + free (s); + return NULL; +} + + +/** + * Counter Mode encryption/decryption (ctr length = 16 bytes) + * with non-padded (truncated) text + */ +static int +do_ctr_crypt (gcry_cipher_hd_t hd, const void *ctr, uint8_t *data, size_t len) +{ + const size_t ctrlen = 16; + div_t d = div (len, ctrlen); + + if (gcry_cipher_setctr (hd, ctr, ctrlen) + || gcry_cipher_encrypt (hd, data, d.quot * ctrlen, NULL, 0)) + return -1; + + if (d.rem) + { + /* Truncated last block */ + uint8_t dummy[ctrlen]; + data += d.quot * ctrlen; + memcpy (dummy, data, d.rem); + memset (dummy + d.rem, 0, ctrlen - d.rem); + + if (gcry_cipher_encrypt (hd, dummy, ctrlen, data, ctrlen)) + return -1; + memcpy (data, dummy, d.rem); + } + + return 0; +} + + +/** + * AES-CM key derivation (saltlen = 14 bytes) + */ +static int +do_derive (gcry_cipher_hd_t prf, const void *salt, + const uint8_t *r, size_t rlen, uint8_t label, + void *out, size_t outlen) +{ + uint8_t iv[16]; + + memcpy (iv, salt, 14); + iv[14] = iv[15] = 0; + + assert (rlen < 14); + iv[13 - rlen] ^= label; + for (size_t i = 0; i < rlen; i++) + iv[sizeof (iv) - rlen + i] ^= r[i]; + + memset (out, 0, outlen); + return do_ctr_crypt (prf, iv, out, outlen); +} + + +/** + * Sets (or resets) the master key and master salt for a SRTP session. + * This must be done at least once before using srtp_send(), srtp_recv(), + * srtcp_send() or srtcp_recv(). Also, rekeying is required every + * 2^48 RTP packets or 2^31 RTCP packets (whichever comes first), + * otherwise the protocol security might be broken. + * + * @return 0 on success, in case of error: + * EINVAL invalid or unsupported key/salt sizes combination + */ +int +srtp_setkey (srtp_session_t *s, const void *key, size_t keylen, + const void *salt, size_t saltlen) +{ + /* SRTP/SRTCP cipher/salt/MAC keys derivation */ + gcry_cipher_hd_t prf; + uint8_t r[6], keybuf[20]; + + if (saltlen != 14) + return EINVAL; + + if (gcry_cipher_open (&prf, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR, 0) + || gcry_cipher_setkey (prf, key, keylen)) + return EINVAL; + + /* SRTP key derivation */ +#if 0 + if (s->kdr != 0) + { + uint64_t index = (((uint64_t)s->rtp_roc) << 16) | s->rtp_seq; + index /= s->kdr; + + for (int i = sizeof (r) - 1; i >= 0; i--) + { + r[i] = index & 0xff; + index = index >> 8; + } + } + else +#endif + memset (r, 0, sizeof (r)); + if (do_derive (prf, salt, r, 6, SRTP_CRYPT, keybuf, 16) + || gcry_cipher_setkey (s->rtp.cipher, keybuf, 16) + || do_derive (prf, salt, r, 6, SRTP_AUTH, keybuf, 20) + || gcry_md_setkey (s->rtp.mac, keybuf, 20) + || do_derive (prf, salt, r, 6, SRTP_SALT, s->rtp.salt, 14)) + return -1; + + /* SRTCP key derivation */ + memcpy (r, &(uint32_t){ htonl (s->rtcp_index) }, 4); + if (do_derive (prf, salt, r, 4, SRTCP_CRYPT, keybuf, 16) + || gcry_cipher_setkey (s->rtcp.cipher, keybuf, 16) + || do_derive (prf, salt, r, 4, SRTCP_AUTH, keybuf, 20) + || gcry_md_setkey (s->rtcp.mac, keybuf, 20) + || do_derive (prf, salt, r, 4, SRTCP_SALT, s->rtcp.salt, 14)) + return -1; + + (void)gcry_cipher_close (prf); + return 0; +} + +static int hexdigit (char c) +{ + if ((c >= '0') && (c <= '9')) + return c - '0'; + if ((c >= 'A') && (c <= 'F')) + return c - 'A' + 0xA; + if ((c >= 'a') && (c <= 'f')) + return c - 'a' + 0xa; + return -1; +} + +static ssize_t hexstring (const char *in, uint8_t *out, size_t outlen) +{ + size_t inlen = strlen (in); + + if ((inlen > (2 * outlen)) || (inlen & 1)) + return -1; + + for (size_t i = 0; i < inlen; i += 2) + { + int a = hexdigit (in[i]), b = hexdigit (in[i + 1]); + if ((a == -1) || (b == -1)) + return -1; + out[i / 2] = (a << 4) | b; + } + return inlen / 2; +} + +/** + * Sets (or resets) the master key and master salt for a SRTP session + * from hexadecimal strings. See also srtp_setkey(). + * + * @return 0 on success, in case of error: + * EINVAL invalid or unsupported key/salt sizes combination + */ +int +srtp_setkeystring (srtp_session_t *s, const char *key, const char *salt) +{ + uint8_t bkey[16]; /* TODO/NOTE: hard-coded for AES */ + uint8_t bsalt[14]; /* TODO/NOTE: hard-coded for the PRF-AES-CM */ + ssize_t bkeylen = hexstring (key, bkey, sizeof (bkey)); + ssize_t bsaltlen = hexstring (salt, bsalt, sizeof (bsalt)); + + if ((bkeylen == -1) || (bsaltlen == -1)) + return EINVAL; + return srtp_setkey (s, bkey, bkeylen, bsalt, bsaltlen) ? EINVAL : 0; +} + +/** + * Sets Roll-over-Counter Carry (RCC) rate for the SRTP session. If not + * specified (through this function), the default rate of ONE is assumed + * (i.e. every RTP packets will carry the RoC). RCC rate is ignored if none + * of the RCC mode has been selected. + * + * The RCC mode is selected through one of these flags for srtp_create(): + * SRTP_RCC_MODE1: integrity protection only for RoC carrying packets + * SRTP_RCC_MODE2: integrity protection for all packets + * SRTP_RCC_MODE3: no integrity protection + * + * RCC mode 3 is insecure. Compared to plain RTP, it provides confidentiality + * (through encryption) but is much more prone to DoS. It can only be used if + * anti-spoofing protection is provided by lower network layers (e.g. IPsec, + * or trusted routers and proper source address filtering). + * + * 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 int +rtp_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 do_ctr_crypt (hd, counter, data, len); +} + + +/** Determines SRTP Roll-Over-Counter (in host-byte order) */ +static uint32_t +srtp_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 (gcry_md_hd_t md, const uint8_t *data, size_t len, + uint32_t roc) +{ + 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); + assert (len >= 12u); + + if ((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 |= UINT64_C(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 |= UINT64_C(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 + * ( will hold the required byte size) + * EACCES packet would trigger a replay error on receiver + */ +int +srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize) +{ + size_t len = *lenp; + size_t tag_len; + size_t roc_len = 0; + + /* Compute required buffer size */ + if (len < 12u) + return EINVAL; + + if (!(s->flags & SRTP_UNAUTHENTICATED)) + { + tag_len = s->tag_len; + + if (rcc_mode (s)) + { + assert (tag_len >= 4); + assert (s->rtp_rcc != 0); + if ((rtp_seq (buf) % s->rtp_rcc) == 0) + { + roc_len = 4; + if (rcc_mode (s) == 3) + tag_len = 0; /* RCC mode 3 -> no auth*/ + else + tag_len -= 4; /* RCC mode 1 or 2 -> auth*/ + } + else + { + if (rcc_mode (s) & 1) + tag_len = 0; /* RCC mode 1 or 3 -> no auth */ + } + } + + *lenp = len + roc_len + tag_len; + } + else + tag_len = 0; + + if (bufsize < *lenp) + return ENOSPC; + + /* Encrypt payload */ + int val = srtp_crypt (s, buf, len); + if (val) + return val; + + /* Authenticate payload */ + if (!(s->flags & SRTP_UNAUTHENTICATED)) + { + uint32_t roc = srtp_compute_roc (s, rtp_seq (buf)); + const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, roc); + + if (roc_len) + { + memcpy (buf + len, &(uint32_t){ htonl (s->rtp_roc) }, 4); + len += 4; + } + memcpy (buf + len, tag, tag_len); +#if 0 + printf ("Sent : 0x"); + for (unsigned i = 0; i < tag_len; i++) + printf ("%02x", tag[i]); + puts (""); +#endif + } + + 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) + */ +int +srtp_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->rtp.mac, 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 int +rtcp_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 |= UINT64_C(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 |= UINT64_C(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) + */ +int +srtcp_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->rtcp.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) + */ +int +srtcp_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->rtcp.mac, buf, len); + if (memcmp (buf + len, tag, s->tag_len)) + return EACCES; + + len -= 4; /* Remove SRTCP index before decryption */ + *lenp = len; + return srtcp_crypt (s, buf, len); +} \ No newline at end of file diff --git a/plugins/crypto-vala/src/srtp.h b/plugins/crypto-vala/src/srtp.h new file mode 100644 index 00000000..abca6988 --- /dev/null +++ b/plugins/crypto-vala/src/srtp.h @@ -0,0 +1,82 @@ +/* + * Secure RTP with libgcrypt + * Copyright (C) 2007 Rémi Denis-Courmont + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public License + * as published by the Free Software Foundation; either version 2.1 + * of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA + ****************************************************************************/ + +#ifndef LIBVLC_SRTP_H +# define LIBVLC_SRTP_H 1 +#include + +typedef struct srtp_session_t srtp_session_t; + +enum +{ + SRTP_UNENCRYPTED=0x1, //< do not encrypt SRTP packets + SRTCP_UNENCRYPTED=0x2, //< do not encrypt SRTCP packets + SRTP_UNAUTHENTICATED=0x4, //< authenticate only SRTCP packets + + SRTP_RCC_MODE1=0x10, //< use Roll-over-Counter Carry mode 1 + SRTP_RCC_MODE2=0x20, //< use Roll-over-Counter Carry mode 2 + SRTP_RCC_MODE3=0x30, //< use Roll-over-Counter Carry mode 3 (insecure) + + SRTP_FLAGS_MASK=0x37 //< mask for valid flags +}; + +/** SRTP encryption algorithms (ciphers); same values as MIKEY */ +enum +{ + SRTP_ENCR_NULL=0, //< no encryption + SRTP_ENCR_AES_CM=1, //< AES counter mode + SRTP_ENCR_AES_F8=2, //< AES F8 mode (not implemented) +}; + +/** SRTP authenticaton algorithms; same values as MIKEY */ +enum +{ + SRTP_AUTH_NULL=0, //< no authentication code + SRTP_AUTH_HMAC_SHA1=1, //< HMAC-SHA1 +}; + +/** SRTP pseudo random function; same values as MIKEY */ +enum +{ + SRTP_PRF_AES_CM=0, //< AES counter mode +}; + +# ifdef __cplusplus +extern "C" { +# endif + +srtp_session_t *srtp_create (int encr, int auth, unsigned tag_len, int prf, + unsigned flags); +void srtp_destroy (srtp_session_t *s); + +int srtp_setkey (srtp_session_t *s, const void *key, size_t keylen, + const void *salt, size_t saltlen); +int srtp_setkeystring (srtp_session_t *s, const char *key, const char *salt); + +void srtp_setrcc_rate (srtp_session_t *s, uint16_t rate); + +int srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t maxsize); +int srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp); +int srtcp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t maxsiz); +int srtcp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp); + +# ifdef __cplusplus +} +# endif +#endif \ No newline at end of file diff --git a/plugins/crypto-vala/src/srtp.vapi b/plugins/crypto-vala/src/srtp.vapi new file mode 100644 index 00000000..0fe825c3 --- /dev/null +++ b/plugins/crypto-vala/src/srtp.vapi @@ -0,0 +1,107 @@ +[CCode (cheader_filename="srtp.h")] +namespace Crypto.Srtp { + +[Compact] +[CCode (cname = "srtp_session_t", free_function = "srtp_destroy")] +public class Session { + [CCode (cname = "srtp_create")] + public Session(Encryption encr, Authentication auth, uint tag_len, Prf prf, Flags flags); + [CCode (cname = "srtp_setkey")] + public int setkey(uint8[] key, uint8[] salt); + [CCode (cname = "srtp_setkeystring")] + public int setkeystring(string key, string salt); + [CCode (cname = "srtp_setrcc_rate")] + public void setrcc_rate(uint16 rate); + + [CCode (cname = "srtp_send")] + private int rtp_send([CCode (array_length = false)] uint8[] buf, ref size_t len, size_t maxsize); + [CCode (cname = "srtcp_send")] + private int rtcp_send([CCode (array_length = false)] uint8[] buf, ref size_t len, size_t maxsize); + [CCode (cname = "srtp_recv")] + private int rtp_recv([CCode (array_length = false)] uint8[] buf, ref size_t len); + [CCode (cname = "srtcp_recv")] + private int rtcp_recv([CCode (array_length = false)] uint8[] buf, ref size_t len); + + public uint8[] encrypt_rtp(uint8[] input, uint tag_len = 10) throws GLib.Error { + uint8[] buf = new uint8[input.length + tag_len]; + GLib.Memory.copy(buf, input, input.length); + size_t buf_use = input.length; + int res = rtp_send(buf, ref buf_use, buf.length); + if (res != 0) { + throw new GLib.Error(-1, res, "RTP encrypt failed"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + public uint8[] encrypt_rtcp(uint8[] input, uint tag_len = 10) throws GLib.Error { + uint8[] buf = new uint8[input.length + tag_len + 4]; + GLib.Memory.copy(buf, input, input.length); + size_t buf_use = input.length; + int res = rtcp_send(buf, ref buf_use, buf.length); + if (res != 0) { + throw new GLib.Error(-1, res, "RTCP encrypt failed"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + public uint8[] decrypt_rtp(uint8[] input) throws GLib.Error { + uint8[] buf = new uint8[input.length]; + GLib.Memory.copy(buf, input, input.length); + size_t buf_use = input.length; + int res = rtp_recv(buf, ref buf_use); + if (res != 0) { + throw new GLib.Error(-1, res, "RTP decrypt failed"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + public uint8[] decrypt_rtcp(uint8[] input) throws GLib.Error { + uint8[] buf = new uint8[input.length]; + GLib.Memory.copy(buf, input, input.length); + size_t buf_use = input.length; + int res = rtcp_recv(buf, ref buf_use); + if (res != 0) { + throw new GLib.Error(-1, res, "RTCP decrypt failed"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } +} + +[Flags] +[CCode (cname = "unsigned", cprefix = "", has_type_id = false)] +public enum Flags { + SRTP_UNENCRYPTED, + SRTCP_UNENCRYPTED, + SRTP_UNAUTHENTICATED, + SRTP_RCC_MODE1, + SRTP_RCC_MODE2, + SRTP_RCC_MODE3 +} + +[CCode (cname = "int", cprefix = "SRTP_ENCR_", has_type_id = false)] +public enum Encryption { + NULL, + AES_CM, + AES_F8 +} + +[CCode (cname = "int", cprefix = "SRTP_AUTH_", has_type_id = false)] +public enum Authentication { + NULL, + HMAC_SHA1 +} + +[CCode (cname = "int", cprefix = "SRTP_PRF_", has_type_id = false)] +public enum Prf { + AES_CM +} + +} \ No newline at end of file diff --git a/plugins/rtp/CMakeLists.txt b/plugins/rtp/CMakeLists.txt index ef2f7698..5311fac3 100644 --- a/plugins/rtp/CMakeLists.txt +++ b/plugins/rtp/CMakeLists.txt @@ -18,11 +18,10 @@ SOURCES src/video_widget.vala src/register_plugin.vala CUSTOM_VAPIS - ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi + ${CMAKE_BINARY_DIR}/exports/crypto.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi - ${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.vapi PACKAGES ${RTP_PACKAGES} OPTIONS @@ -30,7 +29,7 @@ OPTIONS ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="rtp" -I${CMAKE_CURRENT_SOURCE_DIR}/src) -add_library(rtp SHARED ${RTP_VALA_C} src/srtp.c) +add_library(rtp SHARED ${RTP_VALA_C}) target_link_libraries(rtp libdino crypto-vala ${RTP_PACKAGES}) set_target_properties(rtp PROPERTIES PREFIX "") set_target_properties(rtp PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) diff --git a/plugins/rtp/src/srtp.c b/plugins/rtp/src/srtp.c deleted file mode 100644 index 708244d9..00000000 --- a/plugins/rtp/src/srtp.c +++ /dev/null @@ -1,836 +0,0 @@ -/* - * Secure RTP with libgcrypt - * Copyright (C) 2007 Rémi Denis-Courmont - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* TODO: - * Useless stuff (because nothing depends on it): - * - non-nul key derivation rate - * - MKI payload - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include - -#include "srtp.h" - -#include -#include -#include -#include - -#include - -#ifdef _WIN32 -# include -#else -# include -#endif - -#define debug( ... ) (void)0 - -typedef struct srtp_proto_t -{ - gcry_cipher_hd_t cipher; - gcry_md_hd_t mac; - uint64_t window; - uint32_t salt[4]; -} srtp_proto_t; - -struct srtp_session_t -{ - srtp_proto_t rtp; - srtp_proto_t rtcp; - unsigned flags; - unsigned kdr; - uint32_t rtcp_index; - uint32_t rtp_roc; - uint16_t rtp_seq; - uint16_t rtp_rcc; - uint8_t tag_len; -}; - -enum -{ - SRTP_CRYPT, - SRTP_AUTH, - SRTP_SALT, - SRTCP_CRYPT, - SRTCP_AUTH, - SRTCP_SALT -}; - - -static inline unsigned rcc_mode (const srtp_session_t *s) -{ - return (s->flags >> 4) & 3; -} - - -static void proto_destroy (srtp_proto_t *p) -{ - gcry_md_close (p->mac); - gcry_cipher_close (p->cipher); -} - - -/** - * Releases all resources associated with a Secure RTP session. - */ -void srtp_destroy (srtp_session_t *s) -{ - assert (s != NULL); - - proto_destroy (&s->rtcp); - proto_destroy (&s->rtp); - free (s); -} - - -static int proto_create (srtp_proto_t *p, int gcipher, int gmd) -{ - if (gcry_cipher_open (&p->cipher, gcipher, GCRY_CIPHER_MODE_CTR, 0) == 0) - { - if (gcry_md_open (&p->mac, gmd, GCRY_MD_FLAG_HMAC) == 0) - return 0; - gcry_cipher_close (p->cipher); - } - return -1; -} - - -/** - * Allocates a Secure RTP one-way session. - * The same session cannot be used both ways because this would confuse - * internal cryptographic counters; it is however of course feasible to open - * multiple simultaneous sessions with the same master key. - * - * @param encr encryption algorithm number - * @param auth authentication algortihm number - * @param tag_len authentication tag byte length (NOT including RCC) - * @param flags OR'ed optional flags. - * - * @return NULL in case of error - */ -srtp_session_t * -srtp_create (int encr, int auth, unsigned tag_len, int prf, unsigned flags) -{ - if ((flags & ~SRTP_FLAGS_MASK)) - return NULL; - - int cipher, md; - switch (encr) - { - case SRTP_ENCR_NULL: - cipher = GCRY_CIPHER_NONE; - break; - - case SRTP_ENCR_AES_CM: - cipher = GCRY_CIPHER_AES; - break; - - default: - return NULL; - } - - switch (auth) - { - case SRTP_AUTH_NULL: - md = GCRY_MD_NONE; - break; - - case SRTP_AUTH_HMAC_SHA1: - md = GCRY_MD_SHA1; - break; - - default: - return NULL; - } - - if (tag_len > gcry_md_get_algo_dlen (md)) - return NULL; - - if (prf != SRTP_PRF_AES_CM) - return NULL; - - srtp_session_t *s = malloc (sizeof (*s)); - if (s == NULL) - return NULL; - - memset (s, 0, sizeof (*s)); - s->flags = flags; - s->tag_len = tag_len; - s->rtp_rcc = 1; /* Default RCC rate */ - if (rcc_mode (s)) - { - if (tag_len < 4) - goto error; - } - - if (proto_create (&s->rtp, cipher, md) == 0) - { - if (proto_create (&s->rtcp, cipher, md) == 0) - return s; - proto_destroy (&s->rtp); - } - - error: - free (s); - return NULL; -} - - -/** - * Counter Mode encryption/decryption (ctr length = 16 bytes) - * with non-padded (truncated) text - */ -static int -do_ctr_crypt (gcry_cipher_hd_t hd, const void *ctr, uint8_t *data, size_t len) -{ - const size_t ctrlen = 16; - div_t d = div (len, ctrlen); - - if (gcry_cipher_setctr (hd, ctr, ctrlen) - || gcry_cipher_encrypt (hd, data, d.quot * ctrlen, NULL, 0)) - return -1; - - if (d.rem) - { - /* Truncated last block */ - uint8_t dummy[ctrlen]; - data += d.quot * ctrlen; - memcpy (dummy, data, d.rem); - memset (dummy + d.rem, 0, ctrlen - d.rem); - - if (gcry_cipher_encrypt (hd, dummy, ctrlen, data, ctrlen)) - return -1; - memcpy (data, dummy, d.rem); - } - - return 0; -} - - -/** - * AES-CM key derivation (saltlen = 14 bytes) - */ -static int -do_derive (gcry_cipher_hd_t prf, const void *salt, - const uint8_t *r, size_t rlen, uint8_t label, - void *out, size_t outlen) -{ - uint8_t iv[16]; - - memcpy (iv, salt, 14); - iv[14] = iv[15] = 0; - - assert (rlen < 14); - iv[13 - rlen] ^= label; - for (size_t i = 0; i < rlen; i++) - iv[sizeof (iv) - rlen + i] ^= r[i]; - - memset (out, 0, outlen); - return do_ctr_crypt (prf, iv, out, outlen); -} - - -/** - * Sets (or resets) the master key and master salt for a SRTP session. - * This must be done at least once before using srtp_send(), srtp_recv(), - * srtcp_send() or srtcp_recv(). Also, rekeying is required every - * 2^48 RTP packets or 2^31 RTCP packets (whichever comes first), - * otherwise the protocol security might be broken. - * - * @return 0 on success, in case of error: - * EINVAL invalid or unsupported key/salt sizes combination - */ -int -srtp_setkey (srtp_session_t *s, const void *key, size_t keylen, - const void *salt, size_t saltlen) -{ - /* SRTP/SRTCP cipher/salt/MAC keys derivation */ - gcry_cipher_hd_t prf; - uint8_t r[6], keybuf[20]; - - if (saltlen != 14) - return EINVAL; - - if (gcry_cipher_open (&prf, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR, 0) - || gcry_cipher_setkey (prf, key, keylen)) - return EINVAL; - - /* SRTP key derivation */ -#if 0 - if (s->kdr != 0) - { - uint64_t index = (((uint64_t)s->rtp_roc) << 16) | s->rtp_seq; - index /= s->kdr; - - for (int i = sizeof (r) - 1; i >= 0; i--) - { - r[i] = index & 0xff; - index = index >> 8; - } - } - else -#endif - memset (r, 0, sizeof (r)); - if (do_derive (prf, salt, r, 6, SRTP_CRYPT, keybuf, 16) - || gcry_cipher_setkey (s->rtp.cipher, keybuf, 16) - || do_derive (prf, salt, r, 6, SRTP_AUTH, keybuf, 20) - || gcry_md_setkey (s->rtp.mac, keybuf, 20) - || do_derive (prf, salt, r, 6, SRTP_SALT, s->rtp.salt, 14)) - return -1; - - /* SRTCP key derivation */ - memcpy (r, &(uint32_t){ htonl (s->rtcp_index) }, 4); - if (do_derive (prf, salt, r, 4, SRTCP_CRYPT, keybuf, 16) - || gcry_cipher_setkey (s->rtcp.cipher, keybuf, 16) - || do_derive (prf, salt, r, 4, SRTCP_AUTH, keybuf, 20) - || gcry_md_setkey (s->rtcp.mac, keybuf, 20) - || do_derive (prf, salt, r, 4, SRTCP_SALT, s->rtcp.salt, 14)) - return -1; - - (void)gcry_cipher_close (prf); - return 0; -} - -static int hexdigit (char c) -{ - if ((c >= '0') && (c <= '9')) - return c - '0'; - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 0xA; - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 0xa; - return -1; -} - -static ssize_t hexstring (const char *in, uint8_t *out, size_t outlen) -{ - size_t inlen = strlen (in); - - if ((inlen > (2 * outlen)) || (inlen & 1)) - return -1; - - for (size_t i = 0; i < inlen; i += 2) - { - int a = hexdigit (in[i]), b = hexdigit (in[i + 1]); - if ((a == -1) || (b == -1)) - return -1; - out[i / 2] = (a << 4) | b; - } - return inlen / 2; -} - -/** - * Sets (or resets) the master key and master salt for a SRTP session - * from hexadecimal strings. See also srtp_setkey(). - * - * @return 0 on success, in case of error: - * EINVAL invalid or unsupported key/salt sizes combination - */ -int -srtp_setkeystring (srtp_session_t *s, const char *key, const char *salt) -{ - uint8_t bkey[16]; /* TODO/NOTE: hard-coded for AES */ - uint8_t bsalt[14]; /* TODO/NOTE: hard-coded for the PRF-AES-CM */ - ssize_t bkeylen = hexstring (key, bkey, sizeof (bkey)); - ssize_t bsaltlen = hexstring (salt, bsalt, sizeof (bsalt)); - - if ((bkeylen == -1) || (bsaltlen == -1)) - return EINVAL; - return srtp_setkey (s, bkey, bkeylen, bsalt, bsaltlen) ? EINVAL : 0; -} - -/** - * Sets Roll-over-Counter Carry (RCC) rate for the SRTP session. If not - * specified (through this function), the default rate of ONE is assumed - * (i.e. every RTP packets will carry the RoC). RCC rate is ignored if none - * of the RCC mode has been selected. - * - * The RCC mode is selected through one of these flags for srtp_create(): - * SRTP_RCC_MODE1: integrity protection only for RoC carrying packets - * SRTP_RCC_MODE2: integrity protection for all packets - * SRTP_RCC_MODE3: no integrity protection - * - * RCC mode 3 is insecure. Compared to plain RTP, it provides confidentiality - * (through encryption) but is much more prone to DoS. It can only be used if - * anti-spoofing protection is provided by lower network layers (e.g. IPsec, - * or trusted routers and proper source address filtering). - * - * 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 int -rtp_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 do_ctr_crypt (hd, counter, data, len); -} - - -/** Determines SRTP Roll-Over-Counter (in host-byte order) */ -static uint32_t -srtp_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 (gcry_md_hd_t md, const uint8_t *data, size_t len, - uint32_t roc) -{ - 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); - assert (len >= 12u); - - if ((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 |= UINT64_C(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 |= UINT64_C(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 - * ( will hold the required byte size) - * EACCES packet would trigger a replay error on receiver - */ -int -srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize) -{ - size_t len = *lenp; - size_t tag_len; - size_t roc_len = 0; - - /* Compute required buffer size */ - if (len < 12u) - return EINVAL; - - if (!(s->flags & SRTP_UNAUTHENTICATED)) - { - tag_len = s->tag_len; - - if (rcc_mode (s)) - { - assert (tag_len >= 4); - assert (s->rtp_rcc != 0); - if ((rtp_seq (buf) % s->rtp_rcc) == 0) - { - roc_len = 4; - if (rcc_mode (s) == 3) - tag_len = 0; /* RCC mode 3 -> no auth*/ - else - tag_len -= 4; /* RCC mode 1 or 2 -> auth*/ - } - else - { - if (rcc_mode (s) & 1) - tag_len = 0; /* RCC mode 1 or 3 -> no auth */ - } - } - - *lenp = len + roc_len + tag_len; - } - else - tag_len = 0; - - if (bufsize < *lenp) - return ENOSPC; - - /* Encrypt payload */ - int val = srtp_crypt (s, buf, len); - if (val) - return val; - - /* Authenticate payload */ - if (!(s->flags & SRTP_UNAUTHENTICATED)) - { - uint32_t roc = srtp_compute_roc (s, rtp_seq (buf)); - const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, roc); - - if (roc_len) - { - memcpy (buf + len, &(uint32_t){ htonl (s->rtp_roc) }, 4); - len += 4; - } - memcpy (buf + len, tag, tag_len); -#if 0 - printf ("Sent : 0x"); - for (unsigned i = 0; i < tag_len; i++) - printf ("%02x", tag[i]); - puts (""); -#endif - } - - 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) - */ -int -srtp_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->rtp.mac, 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 int -rtcp_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 |= UINT64_C(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 |= UINT64_C(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) - */ -int -srtcp_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->rtcp.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) - */ -int -srtcp_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->rtcp.mac, buf, len); - if (memcmp (buf + len, tag, s->tag_len)) - return EACCES; - - len -= 4; /* Remove SRTCP index before decryption */ - *lenp = len; - return srtcp_crypt (s, buf, len); -} \ No newline at end of file diff --git a/plugins/rtp/src/srtp.h b/plugins/rtp/src/srtp.h deleted file mode 100644 index abca6988..00000000 --- a/plugins/rtp/src/srtp.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Secure RTP with libgcrypt - * Copyright (C) 2007 Rémi Denis-Courmont - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - ****************************************************************************/ - -#ifndef LIBVLC_SRTP_H -# define LIBVLC_SRTP_H 1 -#include - -typedef struct srtp_session_t srtp_session_t; - -enum -{ - SRTP_UNENCRYPTED=0x1, //< do not encrypt SRTP packets - SRTCP_UNENCRYPTED=0x2, //< do not encrypt SRTCP packets - SRTP_UNAUTHENTICATED=0x4, //< authenticate only SRTCP packets - - SRTP_RCC_MODE1=0x10, //< use Roll-over-Counter Carry mode 1 - SRTP_RCC_MODE2=0x20, //< use Roll-over-Counter Carry mode 2 - SRTP_RCC_MODE3=0x30, //< use Roll-over-Counter Carry mode 3 (insecure) - - SRTP_FLAGS_MASK=0x37 //< mask for valid flags -}; - -/** SRTP encryption algorithms (ciphers); same values as MIKEY */ -enum -{ - SRTP_ENCR_NULL=0, //< no encryption - SRTP_ENCR_AES_CM=1, //< AES counter mode - SRTP_ENCR_AES_F8=2, //< AES F8 mode (not implemented) -}; - -/** SRTP authenticaton algorithms; same values as MIKEY */ -enum -{ - SRTP_AUTH_NULL=0, //< no authentication code - SRTP_AUTH_HMAC_SHA1=1, //< HMAC-SHA1 -}; - -/** SRTP pseudo random function; same values as MIKEY */ -enum -{ - SRTP_PRF_AES_CM=0, //< AES counter mode -}; - -# ifdef __cplusplus -extern "C" { -# endif - -srtp_session_t *srtp_create (int encr, int auth, unsigned tag_len, int prf, - unsigned flags); -void srtp_destroy (srtp_session_t *s); - -int srtp_setkey (srtp_session_t *s, const void *key, size_t keylen, - const void *salt, size_t saltlen); -int srtp_setkeystring (srtp_session_t *s, const char *key, const char *salt); - -void srtp_setrcc_rate (srtp_session_t *s, uint16_t rate); - -int srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t maxsize); -int srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp); -int srtcp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t maxsiz); -int srtcp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp); - -# ifdef __cplusplus -} -# endif -#endif \ No newline at end of file diff --git a/plugins/rtp/src/srtp.vapi b/plugins/rtp/src/srtp.vapi deleted file mode 100644 index c5ce7fec..00000000 --- a/plugins/rtp/src/srtp.vapi +++ /dev/null @@ -1,103 +0,0 @@ -[Compact] -[CCode (cname = "srtp_session_t", free_function = "srtp_destroy", cheader_filename="srtp.h")] -public class Dino.Plugins.Rtp.SrtpSession { - [CCode (cname = "srtp_create")] - public SrtpSession(SrtpEncryption encr, SrtpAuthentication auth, uint tag_len, SrtpPrf prf, SrtpFlags flags); - [CCode (cname = "srtp_setkey")] - public int setkey(uint8[] key, uint8[] salt); - [CCode (cname = "srtp_setkeystring")] - public int setkeystring(string key, string salt); - [CCode (cname = "srtp_setrcc_rate")] - public void setrcc_rate(uint16 rate); - - [CCode (cname = "srtp_send")] - private int rtp_send([CCode (array_length = false)] uint8[] buf, ref size_t len, size_t maxsize); - [CCode (cname = "srtcp_send")] - private int rtcp_send([CCode (array_length = false)] uint8[] buf, ref size_t len, size_t maxsize); - [CCode (cname = "srtp_recv")] - private int rtp_recv([CCode (array_length = false)] uint8[] buf, ref size_t len); - [CCode (cname = "srtcp_recv")] - private int rtcp_recv([CCode (array_length = false)] uint8[] buf, ref size_t len); - - public uint8[] encrypt_rtp(uint8[] input, uint tag_len = 10) throws GLib.Error { - uint8[] buf = new uint8[input.length+tag_len]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtp_send(buf, ref buf_use, buf.length); - if (res != 0) { - throw new GLib.Error(-1, res, "RTP encrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } - - public uint8[] encrypt_rtcp(uint8[] input, uint tag_len = 10) throws GLib.Error { - uint8[] buf = new uint8[input.length+tag_len+4]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtcp_send(buf, ref buf_use, buf.length); - if (res != 0) { - throw new GLib.Error(-1, res, "RTCP encrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } - - public uint8[] decrypt_rtp(uint8[] input) throws GLib.Error { - uint8[] buf = new uint8[input.length]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtp_recv(buf, ref buf_use); - if (res != 0) { - throw new GLib.Error(-1, res, "RTP decrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } - - public uint8[] decrypt_rtcp(uint8[] input) throws GLib.Error { - uint8[] buf = new uint8[input.length]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtcp_recv(buf, ref buf_use); - if (res != 0) { - throw new GLib.Error(-1, res, "RTCP decrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } -} - -[Flags] -[CCode (cname = "unsigned", cprefix = "", cheader_filename="srtp.h", has_type_id = false)] -public enum Dino.Plugins.Rtp.SrtpFlags { - SRTP_UNENCRYPTED, - SRTCP_UNENCRYPTED, - SRTP_UNAUTHENTICATED, - - SRTP_RCC_MODE1, - SRTP_RCC_MODE2, - SRTP_RCC_MODE3 -} - -[CCode (cname = "int", cprefix = "SRTP_ENCR_", cheader_filename="srtp.h", has_type_id = false)] -public enum Dino.Plugins.Rtp.SrtpEncryption { - NULL, - AES_CM, - AES_F8 -} - -[CCode (cname = "int", cprefix = "SRTP_AUTH_", cheader_filename="srtp.h", has_type_id = false)] -public enum Dino.Plugins.Rtp.SrtpAuthentication { - NULL, - HMAC_SHA1 -} - -[CCode (cname = "int", cprefix = "SRTP_PRF_", cheader_filename="srtp.h", has_type_id = false)] -public enum Dino.Plugins.Rtp.SrtpPrf { - AES_CM -} \ No newline at end of file diff --git a/plugins/rtp/src/stream.vala b/plugins/rtp/src/stream.vala index 362e2d16..77080a09 100644 --- a/plugins/rtp/src/stream.vala +++ b/plugins/rtp/src/stream.vala @@ -53,8 +53,8 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { private Gst.Pad send_rtp_sink_pad; private Gst.Pad send_rtp_src_pad; - private SrtpSession? local_crypto_session; - private SrtpSession? remote_crypto_session; + private Crypto.Srtp.Session? local_crypto_session; + private Crypto.Srtp.Session? remote_crypto_session; public Stream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(content); @@ -149,11 +149,11 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { private void prepare_local_crypto() { if (local_crypto != null && local_crypto_session == null) { - local_crypto_session = new SrtpSession( - local_crypto.crypto_suite == Xep.JingleRtp.Crypto.F8_128_HMAC_SHA1_80 ? SrtpEncryption.AES_F8 : SrtpEncryption.AES_CM, - SrtpAuthentication.HMAC_SHA1, + local_crypto_session = new Crypto.Srtp.Session( + local_crypto.crypto_suite == Xep.JingleRtp.Crypto.F8_128_HMAC_SHA1_80 ? Crypto.Srtp.Encryption.AES_F8 : Crypto.Srtp.Encryption.AES_CM, + Crypto.Srtp.Authentication.HMAC_SHA1, local_crypto.crypto_suite == Xep.JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_32 ? 4 : 10, - SrtpPrf.AES_CM, + Crypto.Srtp.Prf.AES_CM, 0 ); local_crypto_session.setkey(local_crypto.key, local_crypto.salt); @@ -284,11 +284,11 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { private void prepare_remote_crypto() { if (remote_crypto != null && remote_crypto_session == null) { - remote_crypto_session = new SrtpSession( - remote_crypto.crypto_suite == Xep.JingleRtp.Crypto.F8_128_HMAC_SHA1_80 ? SrtpEncryption.AES_F8 : SrtpEncryption.AES_CM, - SrtpAuthentication.HMAC_SHA1, + remote_crypto_session = new Crypto.Srtp.Session( + remote_crypto.crypto_suite == Xep.JingleRtp.Crypto.F8_128_HMAC_SHA1_80 ? Crypto.Srtp.Encryption.AES_F8 : Crypto.Srtp.Encryption.AES_CM, + Crypto.Srtp.Authentication.HMAC_SHA1, remote_crypto.crypto_suite == Xep.JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_32 ? 4 : 10, - SrtpPrf.AES_CM, + Crypto.Srtp.Prf.AES_CM, 0 ); remote_crypto_session.setkey(remote_crypto.key, remote_crypto.salt); -- cgit v1.2.3-70-g09d2 From 5e58f2988382fffb70602cf308f6686b4731f0da Mon Sep 17 00:00:00 2001 From: Marvin W Date: Mon, 29 Mar 2021 13:20:12 +0200 Subject: Migrate to libsrtp2 --- cmake/FindSrtp2.cmake | 12 + plugins/crypto-vala/CMakeLists.txt | 34 +- plugins/crypto-vala/src/error.vala | 4 +- plugins/crypto-vala/src/srtp.c | 836 --------------------------------- plugins/crypto-vala/src/srtp.h | 82 ---- plugins/crypto-vala/src/srtp.vala | 122 +++++ plugins/crypto-vala/src/srtp.vapi | 107 ----- plugins/crypto-vala/vapi/libsrtp2.vapi | 115 +++++ plugins/ice/CMakeLists.txt | 2 +- plugins/ice/src/dtls_srtp.vala | 49 +- plugins/rtp/CMakeLists.txt | 2 +- plugins/rtp/src/stream.vala | 51 +- 12 files changed, 314 insertions(+), 1102 deletions(-) create mode 100644 cmake/FindSrtp2.cmake delete mode 100644 plugins/crypto-vala/src/srtp.c delete mode 100644 plugins/crypto-vala/src/srtp.h create mode 100644 plugins/crypto-vala/src/srtp.vala delete mode 100644 plugins/crypto-vala/src/srtp.vapi create mode 100644 plugins/crypto-vala/vapi/libsrtp2.vapi (limited to 'plugins/crypto-vala') diff --git a/cmake/FindSrtp2.cmake b/cmake/FindSrtp2.cmake new file mode 100644 index 00000000..40b0ed97 --- /dev/null +++ b/cmake/FindSrtp2.cmake @@ -0,0 +1,12 @@ +include(PkgConfigWithFallback) +find_pkg_config_with_fallback(Srtp2 + PKG_CONFIG_NAME libsrtp2 + LIB_NAMES srtp2 + INCLUDE_NAMES srtp2/srtp.h + INCLUDE_DIR_SUFFIXES srtp2 srtp2/include +) + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Srtp2 + REQUIRED_VARS Srtp2_LIBRARY + VERSION_VAR Srtp2_VERSION) \ No newline at end of file diff --git a/plugins/crypto-vala/CMakeLists.txt b/plugins/crypto-vala/CMakeLists.txt index f97b0d31..4a8da241 100644 --- a/plugins/crypto-vala/CMakeLists.txt +++ b/plugins/crypto-vala/CMakeLists.txt @@ -3,6 +3,7 @@ find_packages(CRYPTO_VALA_PACKAGES REQUIRED GLib GObject GIO + Srtp2 ) vala_precompile(CRYPTO_VALA_C @@ -11,44 +12,23 @@ SOURCES "src/cipher_converter.vala" "src/error.vala" "src/random.vala" - "src/srtp.vapi" + "src/srtp.vala" CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/vapi/gcrypt.vapi" + "${CMAKE_CURRENT_SOURCE_DIR}/vapi/libsrtp2.vapi" PACKAGES ${CRYPTO_VALA_PACKAGES} +OPTIONS + --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi GENERATE_VAPI crypto-vala GENERATE_HEADER crypto-vala ) -add_custom_command(OUTPUT "${CMAKE_BINARY_DIR}/exports/srtp.h" -COMMAND - cp "${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.h" "${CMAKE_BINARY_DIR}/exports/srtp.h" -DEPENDS - "${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.h" -COMMENT - Copy header file srtp.h -) - -add_custom_command(OUTPUT ${CMAKE_BINARY_DIR}/exports/crypto.vapi -COMMAND - cat "${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi" "${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.vapi" > "${CMAKE_BINARY_DIR}/exports/crypto.vapi" -DEPENDS - ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi - ${CMAKE_CURRENT_SOURCE_DIR}/src/srtp.vapi -) - -add_custom_target(crypto-vapi -DEPENDS - ${CMAKE_BINARY_DIR}/exports/crypto.vapi - ${CMAKE_BINARY_DIR}/exports/srtp.h -) - -set(CFLAGS ${VALA_CFLAGS} -I${CMAKE_CURRENT_SOURCE_DIR}/src) +set(CFLAGS ${VALA_CFLAGS}) add_definitions(${CFLAGS}) -add_library(crypto-vala STATIC ${CRYPTO_VALA_C} src/srtp.c) -add_dependencies(crypto-vala crypto-vapi) +add_library(crypto-vala STATIC ${CRYPTO_VALA_C}) target_link_libraries(crypto-vala ${CRYPTO_VALA_PACKAGES} gcrypt) set_property(TARGET crypto-vala PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/plugins/crypto-vala/src/error.vala b/plugins/crypto-vala/src/error.vala index bae4ad08..5007d725 100644 --- a/plugins/crypto-vala/src/error.vala +++ b/plugins/crypto-vala/src/error.vala @@ -2,7 +2,9 @@ namespace Crypto { public errordomain Error { ILLEGAL_ARGUMENTS, - GCRYPT + GCRYPT, + AUTHENTICATION_FAILED, + UNKNOWN } internal void may_throw_gcrypt_error(GCrypt.Error e) throws Error { diff --git a/plugins/crypto-vala/src/srtp.c b/plugins/crypto-vala/src/srtp.c deleted file mode 100644 index 708244d9..00000000 --- a/plugins/crypto-vala/src/srtp.c +++ /dev/null @@ -1,836 +0,0 @@ -/* - * Secure RTP with libgcrypt - * Copyright (C) 2007 Rémi Denis-Courmont - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - */ - -/* TODO: - * Useless stuff (because nothing depends on it): - * - non-nul key derivation rate - * - MKI payload - */ - -#ifdef HAVE_CONFIG_H -# include -#endif - -#include -#include - -#include "srtp.h" - -#include -#include -#include -#include - -#include - -#ifdef _WIN32 -# include -#else -# include -#endif - -#define debug( ... ) (void)0 - -typedef struct srtp_proto_t -{ - gcry_cipher_hd_t cipher; - gcry_md_hd_t mac; - uint64_t window; - uint32_t salt[4]; -} srtp_proto_t; - -struct srtp_session_t -{ - srtp_proto_t rtp; - srtp_proto_t rtcp; - unsigned flags; - unsigned kdr; - uint32_t rtcp_index; - uint32_t rtp_roc; - uint16_t rtp_seq; - uint16_t rtp_rcc; - uint8_t tag_len; -}; - -enum -{ - SRTP_CRYPT, - SRTP_AUTH, - SRTP_SALT, - SRTCP_CRYPT, - SRTCP_AUTH, - SRTCP_SALT -}; - - -static inline unsigned rcc_mode (const srtp_session_t *s) -{ - return (s->flags >> 4) & 3; -} - - -static void proto_destroy (srtp_proto_t *p) -{ - gcry_md_close (p->mac); - gcry_cipher_close (p->cipher); -} - - -/** - * Releases all resources associated with a Secure RTP session. - */ -void srtp_destroy (srtp_session_t *s) -{ - assert (s != NULL); - - proto_destroy (&s->rtcp); - proto_destroy (&s->rtp); - free (s); -} - - -static int proto_create (srtp_proto_t *p, int gcipher, int gmd) -{ - if (gcry_cipher_open (&p->cipher, gcipher, GCRY_CIPHER_MODE_CTR, 0) == 0) - { - if (gcry_md_open (&p->mac, gmd, GCRY_MD_FLAG_HMAC) == 0) - return 0; - gcry_cipher_close (p->cipher); - } - return -1; -} - - -/** - * Allocates a Secure RTP one-way session. - * The same session cannot be used both ways because this would confuse - * internal cryptographic counters; it is however of course feasible to open - * multiple simultaneous sessions with the same master key. - * - * @param encr encryption algorithm number - * @param auth authentication algortihm number - * @param tag_len authentication tag byte length (NOT including RCC) - * @param flags OR'ed optional flags. - * - * @return NULL in case of error - */ -srtp_session_t * -srtp_create (int encr, int auth, unsigned tag_len, int prf, unsigned flags) -{ - if ((flags & ~SRTP_FLAGS_MASK)) - return NULL; - - int cipher, md; - switch (encr) - { - case SRTP_ENCR_NULL: - cipher = GCRY_CIPHER_NONE; - break; - - case SRTP_ENCR_AES_CM: - cipher = GCRY_CIPHER_AES; - break; - - default: - return NULL; - } - - switch (auth) - { - case SRTP_AUTH_NULL: - md = GCRY_MD_NONE; - break; - - case SRTP_AUTH_HMAC_SHA1: - md = GCRY_MD_SHA1; - break; - - default: - return NULL; - } - - if (tag_len > gcry_md_get_algo_dlen (md)) - return NULL; - - if (prf != SRTP_PRF_AES_CM) - return NULL; - - srtp_session_t *s = malloc (sizeof (*s)); - if (s == NULL) - return NULL; - - memset (s, 0, sizeof (*s)); - s->flags = flags; - s->tag_len = tag_len; - s->rtp_rcc = 1; /* Default RCC rate */ - if (rcc_mode (s)) - { - if (tag_len < 4) - goto error; - } - - if (proto_create (&s->rtp, cipher, md) == 0) - { - if (proto_create (&s->rtcp, cipher, md) == 0) - return s; - proto_destroy (&s->rtp); - } - - error: - free (s); - return NULL; -} - - -/** - * Counter Mode encryption/decryption (ctr length = 16 bytes) - * with non-padded (truncated) text - */ -static int -do_ctr_crypt (gcry_cipher_hd_t hd, const void *ctr, uint8_t *data, size_t len) -{ - const size_t ctrlen = 16; - div_t d = div (len, ctrlen); - - if (gcry_cipher_setctr (hd, ctr, ctrlen) - || gcry_cipher_encrypt (hd, data, d.quot * ctrlen, NULL, 0)) - return -1; - - if (d.rem) - { - /* Truncated last block */ - uint8_t dummy[ctrlen]; - data += d.quot * ctrlen; - memcpy (dummy, data, d.rem); - memset (dummy + d.rem, 0, ctrlen - d.rem); - - if (gcry_cipher_encrypt (hd, dummy, ctrlen, data, ctrlen)) - return -1; - memcpy (data, dummy, d.rem); - } - - return 0; -} - - -/** - * AES-CM key derivation (saltlen = 14 bytes) - */ -static int -do_derive (gcry_cipher_hd_t prf, const void *salt, - const uint8_t *r, size_t rlen, uint8_t label, - void *out, size_t outlen) -{ - uint8_t iv[16]; - - memcpy (iv, salt, 14); - iv[14] = iv[15] = 0; - - assert (rlen < 14); - iv[13 - rlen] ^= label; - for (size_t i = 0; i < rlen; i++) - iv[sizeof (iv) - rlen + i] ^= r[i]; - - memset (out, 0, outlen); - return do_ctr_crypt (prf, iv, out, outlen); -} - - -/** - * Sets (or resets) the master key and master salt for a SRTP session. - * This must be done at least once before using srtp_send(), srtp_recv(), - * srtcp_send() or srtcp_recv(). Also, rekeying is required every - * 2^48 RTP packets or 2^31 RTCP packets (whichever comes first), - * otherwise the protocol security might be broken. - * - * @return 0 on success, in case of error: - * EINVAL invalid or unsupported key/salt sizes combination - */ -int -srtp_setkey (srtp_session_t *s, const void *key, size_t keylen, - const void *salt, size_t saltlen) -{ - /* SRTP/SRTCP cipher/salt/MAC keys derivation */ - gcry_cipher_hd_t prf; - uint8_t r[6], keybuf[20]; - - if (saltlen != 14) - return EINVAL; - - if (gcry_cipher_open (&prf, GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CTR, 0) - || gcry_cipher_setkey (prf, key, keylen)) - return EINVAL; - - /* SRTP key derivation */ -#if 0 - if (s->kdr != 0) - { - uint64_t index = (((uint64_t)s->rtp_roc) << 16) | s->rtp_seq; - index /= s->kdr; - - for (int i = sizeof (r) - 1; i >= 0; i--) - { - r[i] = index & 0xff; - index = index >> 8; - } - } - else -#endif - memset (r, 0, sizeof (r)); - if (do_derive (prf, salt, r, 6, SRTP_CRYPT, keybuf, 16) - || gcry_cipher_setkey (s->rtp.cipher, keybuf, 16) - || do_derive (prf, salt, r, 6, SRTP_AUTH, keybuf, 20) - || gcry_md_setkey (s->rtp.mac, keybuf, 20) - || do_derive (prf, salt, r, 6, SRTP_SALT, s->rtp.salt, 14)) - return -1; - - /* SRTCP key derivation */ - memcpy (r, &(uint32_t){ htonl (s->rtcp_index) }, 4); - if (do_derive (prf, salt, r, 4, SRTCP_CRYPT, keybuf, 16) - || gcry_cipher_setkey (s->rtcp.cipher, keybuf, 16) - || do_derive (prf, salt, r, 4, SRTCP_AUTH, keybuf, 20) - || gcry_md_setkey (s->rtcp.mac, keybuf, 20) - || do_derive (prf, salt, r, 4, SRTCP_SALT, s->rtcp.salt, 14)) - return -1; - - (void)gcry_cipher_close (prf); - return 0; -} - -static int hexdigit (char c) -{ - if ((c >= '0') && (c <= '9')) - return c - '0'; - if ((c >= 'A') && (c <= 'F')) - return c - 'A' + 0xA; - if ((c >= 'a') && (c <= 'f')) - return c - 'a' + 0xa; - return -1; -} - -static ssize_t hexstring (const char *in, uint8_t *out, size_t outlen) -{ - size_t inlen = strlen (in); - - if ((inlen > (2 * outlen)) || (inlen & 1)) - return -1; - - for (size_t i = 0; i < inlen; i += 2) - { - int a = hexdigit (in[i]), b = hexdigit (in[i + 1]); - if ((a == -1) || (b == -1)) - return -1; - out[i / 2] = (a << 4) | b; - } - return inlen / 2; -} - -/** - * Sets (or resets) the master key and master salt for a SRTP session - * from hexadecimal strings. See also srtp_setkey(). - * - * @return 0 on success, in case of error: - * EINVAL invalid or unsupported key/salt sizes combination - */ -int -srtp_setkeystring (srtp_session_t *s, const char *key, const char *salt) -{ - uint8_t bkey[16]; /* TODO/NOTE: hard-coded for AES */ - uint8_t bsalt[14]; /* TODO/NOTE: hard-coded for the PRF-AES-CM */ - ssize_t bkeylen = hexstring (key, bkey, sizeof (bkey)); - ssize_t bsaltlen = hexstring (salt, bsalt, sizeof (bsalt)); - - if ((bkeylen == -1) || (bsaltlen == -1)) - return EINVAL; - return srtp_setkey (s, bkey, bkeylen, bsalt, bsaltlen) ? EINVAL : 0; -} - -/** - * Sets Roll-over-Counter Carry (RCC) rate for the SRTP session. If not - * specified (through this function), the default rate of ONE is assumed - * (i.e. every RTP packets will carry the RoC). RCC rate is ignored if none - * of the RCC mode has been selected. - * - * The RCC mode is selected through one of these flags for srtp_create(): - * SRTP_RCC_MODE1: integrity protection only for RoC carrying packets - * SRTP_RCC_MODE2: integrity protection for all packets - * SRTP_RCC_MODE3: no integrity protection - * - * RCC mode 3 is insecure. Compared to plain RTP, it provides confidentiality - * (through encryption) but is much more prone to DoS. It can only be used if - * anti-spoofing protection is provided by lower network layers (e.g. IPsec, - * or trusted routers and proper source address filtering). - * - * 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 int -rtp_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 do_ctr_crypt (hd, counter, data, len); -} - - -/** Determines SRTP Roll-Over-Counter (in host-byte order) */ -static uint32_t -srtp_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 (gcry_md_hd_t md, const uint8_t *data, size_t len, - uint32_t roc) -{ - 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); - assert (len >= 12u); - - if ((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 |= UINT64_C(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 |= UINT64_C(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 - * ( will hold the required byte size) - * EACCES packet would trigger a replay error on receiver - */ -int -srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t bufsize) -{ - size_t len = *lenp; - size_t tag_len; - size_t roc_len = 0; - - /* Compute required buffer size */ - if (len < 12u) - return EINVAL; - - if (!(s->flags & SRTP_UNAUTHENTICATED)) - { - tag_len = s->tag_len; - - if (rcc_mode (s)) - { - assert (tag_len >= 4); - assert (s->rtp_rcc != 0); - if ((rtp_seq (buf) % s->rtp_rcc) == 0) - { - roc_len = 4; - if (rcc_mode (s) == 3) - tag_len = 0; /* RCC mode 3 -> no auth*/ - else - tag_len -= 4; /* RCC mode 1 or 2 -> auth*/ - } - else - { - if (rcc_mode (s) & 1) - tag_len = 0; /* RCC mode 1 or 3 -> no auth */ - } - } - - *lenp = len + roc_len + tag_len; - } - else - tag_len = 0; - - if (bufsize < *lenp) - return ENOSPC; - - /* Encrypt payload */ - int val = srtp_crypt (s, buf, len); - if (val) - return val; - - /* Authenticate payload */ - if (!(s->flags & SRTP_UNAUTHENTICATED)) - { - uint32_t roc = srtp_compute_roc (s, rtp_seq (buf)); - const uint8_t *tag = rtp_digest (s->rtp.mac, buf, len, roc); - - if (roc_len) - { - memcpy (buf + len, &(uint32_t){ htonl (s->rtp_roc) }, 4); - len += 4; - } - memcpy (buf + len, tag, tag_len); -#if 0 - printf ("Sent : 0x"); - for (unsigned i = 0; i < tag_len; i++) - printf ("%02x", tag[i]); - puts (""); -#endif - } - - 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) - */ -int -srtp_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->rtp.mac, 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 int -rtcp_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 |= UINT64_C(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 |= UINT64_C(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) - */ -int -srtcp_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->rtcp.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) - */ -int -srtcp_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->rtcp.mac, buf, len); - if (memcmp (buf + len, tag, s->tag_len)) - return EACCES; - - len -= 4; /* Remove SRTCP index before decryption */ - *lenp = len; - return srtcp_crypt (s, buf, len); -} \ No newline at end of file diff --git a/plugins/crypto-vala/src/srtp.h b/plugins/crypto-vala/src/srtp.h deleted file mode 100644 index abca6988..00000000 --- a/plugins/crypto-vala/src/srtp.h +++ /dev/null @@ -1,82 +0,0 @@ -/* - * Secure RTP with libgcrypt - * Copyright (C) 2007 Rémi Denis-Courmont - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public License - * as published by the Free Software Foundation; either version 2.1 - * of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA - ****************************************************************************/ - -#ifndef LIBVLC_SRTP_H -# define LIBVLC_SRTP_H 1 -#include - -typedef struct srtp_session_t srtp_session_t; - -enum -{ - SRTP_UNENCRYPTED=0x1, //< do not encrypt SRTP packets - SRTCP_UNENCRYPTED=0x2, //< do not encrypt SRTCP packets - SRTP_UNAUTHENTICATED=0x4, //< authenticate only SRTCP packets - - SRTP_RCC_MODE1=0x10, //< use Roll-over-Counter Carry mode 1 - SRTP_RCC_MODE2=0x20, //< use Roll-over-Counter Carry mode 2 - SRTP_RCC_MODE3=0x30, //< use Roll-over-Counter Carry mode 3 (insecure) - - SRTP_FLAGS_MASK=0x37 //< mask for valid flags -}; - -/** SRTP encryption algorithms (ciphers); same values as MIKEY */ -enum -{ - SRTP_ENCR_NULL=0, //< no encryption - SRTP_ENCR_AES_CM=1, //< AES counter mode - SRTP_ENCR_AES_F8=2, //< AES F8 mode (not implemented) -}; - -/** SRTP authenticaton algorithms; same values as MIKEY */ -enum -{ - SRTP_AUTH_NULL=0, //< no authentication code - SRTP_AUTH_HMAC_SHA1=1, //< HMAC-SHA1 -}; - -/** SRTP pseudo random function; same values as MIKEY */ -enum -{ - SRTP_PRF_AES_CM=0, //< AES counter mode -}; - -# ifdef __cplusplus -extern "C" { -# endif - -srtp_session_t *srtp_create (int encr, int auth, unsigned tag_len, int prf, - unsigned flags); -void srtp_destroy (srtp_session_t *s); - -int srtp_setkey (srtp_session_t *s, const void *key, size_t keylen, - const void *salt, size_t saltlen); -int srtp_setkeystring (srtp_session_t *s, const char *key, const char *salt); - -void srtp_setrcc_rate (srtp_session_t *s, uint16_t rate); - -int srtp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t maxsize); -int srtp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp); -int srtcp_send (srtp_session_t *s, uint8_t *buf, size_t *lenp, size_t maxsiz); -int srtcp_recv (srtp_session_t *s, uint8_t *buf, size_t *lenp); - -# ifdef __cplusplus -} -# endif -#endif \ No newline at end of file diff --git a/plugins/crypto-vala/src/srtp.vala b/plugins/crypto-vala/src/srtp.vala new file mode 100644 index 00000000..77b5acde --- /dev/null +++ b/plugins/crypto-vala/src/srtp.vala @@ -0,0 +1,122 @@ +using Srtp; + +public class Crypto.Srtp { + public const string AES_CM_128_HMAC_SHA1_80 = "AES_CM_128_HMAC_SHA1_80"; + public const string AES_CM_128_HMAC_SHA1_32 = "AES_CM_128_HMAC_SHA1_32"; + public const string F8_128_HMAC_SHA1_80 = "F8_128_HMAC_SHA1_80"; + + public class Session { + public bool has_encrypt { get; private set; } + public bool has_decrypt { get; private set; } + + private Context encrypt_context; + private Context decrypt_context; + + static construct { + init(); + install_log_handler(log); + } + + private static void log(LogLevel level, string msg) { + print(@"SRTP[$level]: $msg\n"); + } + + public Session() { + Context.create(out encrypt_context, null); + Context.create(out decrypt_context, null); + } + + public uint8[] encrypt_rtp(uint8[] data) throws Error { + uint8[] buf = new uint8[data.length + MAX_TRAILER_LEN]; + Memory.copy(buf, data, data.length); + int buf_use = data.length; + ErrorStatus res = encrypt_context.protect(buf, ref buf_use); + if (res != ErrorStatus.ok) { + throw new Error.UNKNOWN(@"SRTP encrypt failed: $res"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + public uint8[] decrypt_rtp(uint8[] data) throws Error { + uint8[] buf = new uint8[data.length]; + Memory.copy(buf, data, data.length); + int buf_use = data.length; + ErrorStatus res = decrypt_context.unprotect(buf, ref buf_use); + switch (res) { + case ErrorStatus.auth_fail: + throw new Error.AUTHENTICATION_FAILED("SRTP packet failed the message authentication check"); + case ErrorStatus.ok: + break; + default: + throw new Error.UNKNOWN(@"SRTP decrypt failed: $res"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + public uint8[] encrypt_rtcp(uint8[] data) throws Error { + uint8[] buf = new uint8[data.length + MAX_TRAILER_LEN + 4]; + Memory.copy(buf, data, data.length); + int buf_use = data.length; + ErrorStatus res = encrypt_context.protect_rtcp(buf, ref buf_use); + if (res != ErrorStatus.ok) { + throw new Error.UNKNOWN(@"SRTCP encrypt failed: $res"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + public uint8[] decrypt_rtcp(uint8[] data) throws Error { + uint8[] buf = new uint8[data.length]; + Memory.copy(buf, data, data.length); + int buf_use = data.length; + ErrorStatus res = decrypt_context.unprotect_rtcp(buf, ref buf_use); + switch (res) { + case ErrorStatus.auth_fail: + throw new Error.AUTHENTICATION_FAILED("SRTCP packet failed the message authentication check"); + case ErrorStatus.ok: + break; + default: + throw new Error.UNKNOWN(@"SRTP decrypt failed: $res"); + } + uint8[] ret = new uint8[buf_use]; + GLib.Memory.copy(ret, buf, buf_use); + return ret; + } + + private Policy create_policy(string profile) { + Policy policy = Policy(); + switch (profile) { + case AES_CM_128_HMAC_SHA1_80: + policy.rtp.set_aes_cm_128_hmac_sha1_80(); + policy.rtcp.set_aes_cm_128_hmac_sha1_80(); + break; + } + return policy; + } + + public void set_encryption_key(string profile, uint8[] key, uint8[] salt) { + Policy policy = create_policy(profile); + policy.ssrc.type = SsrcType.any_outbound; + policy.key = new uint8[key.length + salt.length]; + Memory.copy(policy.key, key, key.length); + Memory.copy(((uint8*)policy.key) + key.length, salt, salt.length); + encrypt_context.add_stream(ref policy); + has_encrypt = true; + } + + public void set_decryption_key(string profile, uint8[] key, uint8[] salt) { + Policy policy = create_policy(profile); + policy.ssrc.type = SsrcType.any_inbound; + policy.key = new uint8[key.length + salt.length]; + Memory.copy(policy.key, key, key.length); + Memory.copy(((uint8*)policy.key) + key.length, salt, salt.length); + decrypt_context.add_stream(ref policy); + has_decrypt = true; + } + } +} \ No newline at end of file diff --git a/plugins/crypto-vala/src/srtp.vapi b/plugins/crypto-vala/src/srtp.vapi deleted file mode 100644 index 0fe825c3..00000000 --- a/plugins/crypto-vala/src/srtp.vapi +++ /dev/null @@ -1,107 +0,0 @@ -[CCode (cheader_filename="srtp.h")] -namespace Crypto.Srtp { - -[Compact] -[CCode (cname = "srtp_session_t", free_function = "srtp_destroy")] -public class Session { - [CCode (cname = "srtp_create")] - public Session(Encryption encr, Authentication auth, uint tag_len, Prf prf, Flags flags); - [CCode (cname = "srtp_setkey")] - public int setkey(uint8[] key, uint8[] salt); - [CCode (cname = "srtp_setkeystring")] - public int setkeystring(string key, string salt); - [CCode (cname = "srtp_setrcc_rate")] - public void setrcc_rate(uint16 rate); - - [CCode (cname = "srtp_send")] - private int rtp_send([CCode (array_length = false)] uint8[] buf, ref size_t len, size_t maxsize); - [CCode (cname = "srtcp_send")] - private int rtcp_send([CCode (array_length = false)] uint8[] buf, ref size_t len, size_t maxsize); - [CCode (cname = "srtp_recv")] - private int rtp_recv([CCode (array_length = false)] uint8[] buf, ref size_t len); - [CCode (cname = "srtcp_recv")] - private int rtcp_recv([CCode (array_length = false)] uint8[] buf, ref size_t len); - - public uint8[] encrypt_rtp(uint8[] input, uint tag_len = 10) throws GLib.Error { - uint8[] buf = new uint8[input.length + tag_len]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtp_send(buf, ref buf_use, buf.length); - if (res != 0) { - throw new GLib.Error(-1, res, "RTP encrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } - - public uint8[] encrypt_rtcp(uint8[] input, uint tag_len = 10) throws GLib.Error { - uint8[] buf = new uint8[input.length + tag_len + 4]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtcp_send(buf, ref buf_use, buf.length); - if (res != 0) { - throw new GLib.Error(-1, res, "RTCP encrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } - - public uint8[] decrypt_rtp(uint8[] input) throws GLib.Error { - uint8[] buf = new uint8[input.length]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtp_recv(buf, ref buf_use); - if (res != 0) { - throw new GLib.Error(-1, res, "RTP decrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } - - public uint8[] decrypt_rtcp(uint8[] input) throws GLib.Error { - uint8[] buf = new uint8[input.length]; - GLib.Memory.copy(buf, input, input.length); - size_t buf_use = input.length; - int res = rtcp_recv(buf, ref buf_use); - if (res != 0) { - throw new GLib.Error(-1, res, "RTCP decrypt failed"); - } - uint8[] ret = new uint8[buf_use]; - GLib.Memory.copy(ret, buf, buf_use); - return ret; - } -} - -[Flags] -[CCode (cname = "unsigned", cprefix = "", has_type_id = false)] -public enum Flags { - SRTP_UNENCRYPTED, - SRTCP_UNENCRYPTED, - SRTP_UNAUTHENTICATED, - SRTP_RCC_MODE1, - SRTP_RCC_MODE2, - SRTP_RCC_MODE3 -} - -[CCode (cname = "int", cprefix = "SRTP_ENCR_", has_type_id = false)] -public enum Encryption { - NULL, - AES_CM, - AES_F8 -} - -[CCode (cname = "int", cprefix = "SRTP_AUTH_", has_type_id = false)] -public enum Authentication { - NULL, - HMAC_SHA1 -} - -[CCode (cname = "int", cprefix = "SRTP_PRF_", has_type_id = false)] -public enum Prf { - AES_CM -} - -} \ No newline at end of file diff --git a/plugins/crypto-vala/vapi/libsrtp2.vapi b/plugins/crypto-vala/vapi/libsrtp2.vapi new file mode 100644 index 00000000..5ceedced --- /dev/null +++ b/plugins/crypto-vala/vapi/libsrtp2.vapi @@ -0,0 +1,115 @@ +[CCode (cheader_filename = "srtp2/srtp.h")] +namespace Srtp { +public const uint MAX_TRAILER_LEN; + +public static ErrorStatus init(); +public static ErrorStatus shutdown(); + +[Compact] +[CCode (cname = "srtp_ctx_t", cprefix = "srtp_", free_function = "srtp_dealloc")] +public class Context { + public static ErrorStatus create(out Context session, Policy? policy); + + public ErrorStatus protect([CCode (type = "void*", array_length = false)] uint8[] rtp, ref int len); + public ErrorStatus unprotect([CCode (type = "void*", array_length = false)] uint8[] rtp, ref int len); + + public ErrorStatus protect_rtcp([CCode (type = "void*", array_length = false)] uint8[] rtcp, ref int len); + public ErrorStatus unprotect_rtcp([CCode (type = "void*", array_length = false)] uint8[] rtcp, ref int len); + + public ErrorStatus add_stream(ref Policy policy); + public ErrorStatus update_stream(ref Policy policy); + public ErrorStatus remove_stream(uint ssrc); + public ErrorStatus update(ref Policy policy); +} + +[CCode (cname = "srtp_ssrc_t")] +public struct Ssrc { + public SsrcType type; + public uint value; +} + +[CCode (cname = "srtp_ssrc_type_t", cprefix = "ssrc_")] +public enum SsrcType { + undefined, specific, any_inbound, any_outbound +} + +[CCode (cname = "srtp_policy_t", destroy_function = "")] +public struct Policy { + public Ssrc ssrc; + public CryptoPolicy rtp; + public CryptoPolicy rtcp; + [CCode (array_length = false)] + public uint8[] key; + public ulong num_master_keys; + public ulong window_size; + public int allow_repeat_tx; + [CCode (array_length_cname = "enc_xtn_hdr_count")] + public int[] enc_xtn_hdr; +} + +[CCode (cname = "srtp_crypto_policy_t")] +public struct CryptoPolicy { + public CipherType cipher_type; + public int cipher_key_len; + public AuthType auth_type; + public int auth_key_len; + public int auth_tag_len; + public SecurityServices sec_serv; + + public void set_aes_cm_128_hmac_sha1_80(); + public void set_aes_cm_128_hmac_sha1_32(); + public void set_aes_cm_128_null_auth(); + public void set_aes_cm_192_hmac_sha1_32(); + public void set_aes_cm_192_hmac_sha1_80(); + public void set_aes_cm_192_null_auth(); + public void set_aes_cm_256_hmac_sha1_32(); + public void set_aes_cm_256_hmac_sha1_80(); + public void set_aes_cm_256_null_auth(); + public void set_aes_gcm_128_16_auth(); + public void set_aes_gcm_128_8_auth(); + public void set_aes_gcm_128_8_only_auth(); + public void set_aes_gcm_256_16_auth(); + public void set_aes_gcm_256_8_auth(); + public void set_aes_gcm_256_8_only_auth(); + public void set_null_cipher_hmac_null(); + public void set_null_cipher_hmac_sha1_80(); + + public void set_rtp_default(); + public void set_rtcp_default(); + + public void set_from_profile_for_rtp(Profile profile); + public void set_from_profile_for_rtcp(Profile profile); +} + +[CCode (cname = "srtp_profile_t", cprefix = "srtp_profile_")] +public enum Profile { + reserved, aes128_cm_sha1_80, aes128_cm_sha1_32, null_sha1_80, null_sha1_32, aead_aes_128_gcm, aead_aes_256_gcm +} + +[CCode (cname = "srtp_cipher_type_id_t")] +public struct CipherType : uint32 {} + +[CCode (cname = "srtp_auth_type_id_t")] +public struct AuthType : uint32 {} + +[CCode (cname = "srtp_sec_serv_t", cprefix = "sec_serv_")] +public enum SecurityServices { + none, conf, auth, conf_and_auth; +} + +[CCode (cname = "srtp_err_status_t", cprefix = "srtp_err_status_", has_type_id = false)] +public enum ErrorStatus { + ok, fail, bad_param, alloc_fail, dealloc_fail, init_fail, terminus, auth_fail, cipher_fail, replay_fail, algo_fail, no_such_op, no_ctx, cant_check, key_expired, socket_err, signal_err, nonce_bad, encode_err, semaphore_err, pfkey_err, bad_mki, pkt_idx_old, pkt_idx_adv +} + +[CCode (cname = "srtp_log_level_t", cprefix = "srtp_log_level_", has_type_id = false)] +public enum LogLevel { + error, warning, info, debug +} + +[CCode (cname = "srtp_log_handler_func_t")] +public delegate void LogHandler(LogLevel level, string msg); + +public static ErrorStatus install_log_handler(LogHandler func); + +} \ No newline at end of file diff --git a/plugins/ice/CMakeLists.txt b/plugins/ice/CMakeLists.txt index 38025aa0..392a202f 100644 --- a/plugins/ice/CMakeLists.txt +++ b/plugins/ice/CMakeLists.txt @@ -20,7 +20,7 @@ CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi - ${CMAKE_BINARY_DIR}/exports/crypto.vapi + ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi PACKAGES ${ICE_PACKAGES} OPTIONS diff --git a/plugins/ice/src/dtls_srtp.vala b/plugins/ice/src/dtls_srtp.vala index a21c242b..b742ccab 100644 --- a/plugins/ice/src/dtls_srtp.vala +++ b/plugins/ice/src/dtls_srtp.vala @@ -12,8 +12,7 @@ public class DtlsSrtp { private uint pull_timeout = uint.MAX; private string peer_fingerprint; - private Crypto.Srtp.Session encrypt_session; - private Crypto.Srtp.Session decrypt_session; + private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session(); public static DtlsSrtp setup() throws GLib.Error { var obj = new DtlsSrtp(); @@ -30,9 +29,19 @@ public class DtlsSrtp { } public uint8[] process_incoming_data(uint component_id, uint8[] data) { - if (decrypt_session != null) { - if (component_id == 1) return decrypt_session.decrypt_rtp(data); - if (component_id == 2) return decrypt_session.decrypt_rtcp(data); + if (srtp_session.has_decrypt) { + try { + if (component_id == 1) { + if (data.length >= 2 && data[1] >= 192 && data[1] < 224) { + return srtp_session.decrypt_rtcp(data); + } + return srtp_session.decrypt_rtp(data); + } + if (component_id == 2) return srtp_session.decrypt_rtcp(data); + } catch (Error e) { + warning("%s (%d)", e.message, e.code); + return null; + } } else if (component_id == 1) { on_data_rec(data); } @@ -40,9 +49,19 @@ public class DtlsSrtp { } public uint8[] process_outgoing_data(uint component_id, uint8[] data) { - if (encrypt_session != null) { - if (component_id == 1) return encrypt_session.encrypt_rtp(data); - if (component_id == 2) return encrypt_session.encrypt_rtcp(data); + if (srtp_session.has_encrypt) { + try { + if (component_id == 1) { + if (data.length >= 2 && data[1] >= 192 && data[1] < 224) { + return srtp_session.encrypt_rtcp(data); + } + return srtp_session.encrypt_rtp(data); + } + if (component_id == 2) return srtp_session.encrypt_rtcp(data); + } catch (Error e) { + warning("%s (%d)", e.message, e.code); + return null; + } } return null; } @@ -123,19 +142,13 @@ public class DtlsSrtp { warning("SRTP client/server key/salt null"); } - Crypto.Srtp.Session encrypt_session = new Crypto.Srtp.Session(Crypto.Srtp.Encryption.AES_CM, Crypto.Srtp.Authentication.HMAC_SHA1, 10, Crypto.Srtp.Prf.AES_CM, 0); - Crypto.Srtp.Session decrypt_session = new Crypto.Srtp.Session(Crypto.Srtp.Encryption.AES_CM, Crypto.Srtp.Authentication.HMAC_SHA1, 10, Crypto.Srtp.Prf.AES_CM, 0); - if (server) { - encrypt_session.setkey(server_key.extract(), server_salt.extract()); - decrypt_session.setkey(client_key.extract(), client_salt.extract()); + srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); + srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); } else { - encrypt_session.setkey(client_key.extract(), client_salt.extract()); - decrypt_session.setkey(server_key.extract(), server_salt.extract()); + srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); + srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); } - - this.encrypt_session = (owned)encrypt_session; - this.decrypt_session = (owned)decrypt_session; } private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) { diff --git a/plugins/rtp/CMakeLists.txt b/plugins/rtp/CMakeLists.txt index 8ce2a7c6..c6888459 100644 --- a/plugins/rtp/CMakeLists.txt +++ b/plugins/rtp/CMakeLists.txt @@ -19,7 +19,7 @@ SOURCES src/video_widget.vala src/register_plugin.vala CUSTOM_VAPIS - ${CMAKE_BINARY_DIR}/exports/crypto.vapi + ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi diff --git a/plugins/rtp/src/stream.vala b/plugins/rtp/src/stream.vala index 77080a09..bedd6f8a 100644 --- a/plugins/rtp/src/stream.vala +++ b/plugins/rtp/src/stream.vala @@ -53,8 +53,7 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { private Gst.Pad send_rtp_sink_pad; private Gst.Pad send_rtp_src_pad; - private Crypto.Srtp.Session? local_crypto_session; - private Crypto.Srtp.Session? remote_crypto_session; + private Crypto.Srtp.Session? crypto_session = new Crypto.Srtp.Session(); public Stream(Plugin plugin, Xmpp.Xep.Jingle.Content content) { base(content); @@ -148,15 +147,8 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { } private void prepare_local_crypto() { - if (local_crypto != null && local_crypto_session == null) { - local_crypto_session = new Crypto.Srtp.Session( - local_crypto.crypto_suite == Xep.JingleRtp.Crypto.F8_128_HMAC_SHA1_80 ? Crypto.Srtp.Encryption.AES_F8 : Crypto.Srtp.Encryption.AES_CM, - Crypto.Srtp.Authentication.HMAC_SHA1, - local_crypto.crypto_suite == Xep.JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_32 ? 4 : 10, - Crypto.Srtp.Prf.AES_CM, - 0 - ); - local_crypto_session.setkey(local_crypto.key, local_crypto.salt); + if (local_crypto != null && !crypto_session.has_encrypt) { + crypto_session.set_encryption_key(local_crypto.crypto_suite, local_crypto.key, local_crypto.salt); debug("Setting up encryption with key params %s", local_crypto.key_params); } } @@ -172,15 +164,19 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { buffer.extract_dup(0, buffer.get_size(), out data); prepare_local_crypto(); if (sink == send_rtp) { - if (local_crypto_session != null) { - data = local_crypto_session.encrypt_rtp(data, local_crypto.crypto_suite == Xep.JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_32 ? 4 : 10); + if (crypto_session.has_encrypt) { + data = crypto_session.encrypt_rtp(data); } on_send_rtp_data(new Bytes.take(data)); } else if (sink == send_rtcp) { - if (local_crypto_session != null) { - data = local_crypto_session.encrypt_rtcp(data, local_crypto.crypto_suite == Xep.JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_32 ? 4 : 10); + if (crypto_session.has_encrypt) { + data = crypto_session.encrypt_rtcp(data); + } + if (rtcp_mux) { + on_send_rtp_data(new Bytes.take(data)); + } else { + on_send_rtcp_data(new Bytes.take(data)); } - on_send_rtcp_data(new Bytes.take(data)); } else { warning("unknown sample"); } @@ -283,25 +279,22 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { } private void prepare_remote_crypto() { - if (remote_crypto != null && remote_crypto_session == null) { - remote_crypto_session = new Crypto.Srtp.Session( - remote_crypto.crypto_suite == Xep.JingleRtp.Crypto.F8_128_HMAC_SHA1_80 ? Crypto.Srtp.Encryption.AES_F8 : Crypto.Srtp.Encryption.AES_CM, - Crypto.Srtp.Authentication.HMAC_SHA1, - remote_crypto.crypto_suite == Xep.JingleRtp.Crypto.AES_CM_128_HMAC_SHA1_32 ? 4 : 10, - Crypto.Srtp.Prf.AES_CM, - 0 - ); - remote_crypto_session.setkey(remote_crypto.key, remote_crypto.salt); + if (remote_crypto != null && crypto_session.has_decrypt) { + crypto_session.set_decryption_key(remote_crypto.crypto_suite, remote_crypto.key, remote_crypto.salt); debug("Setting up decryption with key params %s", remote_crypto.key_params); } } public override void on_recv_rtp_data(Bytes bytes) { + if (rtcp_mux && bytes.length >= 2 && bytes.get(1) >= 192 && bytes.get(1) < 224) { + on_recv_rtcp_data(bytes); + return; + } prepare_remote_crypto(); uint8[] data = bytes.get_data(); - if (remote_crypto_session != null) { + if (crypto_session.has_decrypt) { try { - data = remote_crypto_session.decrypt_rtp(data); + data = crypto_session.decrypt_rtp(data); } catch (Error e) { warning("%s (%d)", e.message, e.code); } @@ -314,9 +307,9 @@ public class Dino.Plugins.Rtp.Stream : Xmpp.Xep.JingleRtp.Stream { public override void on_recv_rtcp_data(Bytes bytes) { prepare_remote_crypto(); uint8[] data = bytes.get_data(); - if (remote_crypto_session != null) { + if (crypto_session.has_decrypt) { try { - data = remote_crypto_session.decrypt_rtcp(data); + data = crypto_session.decrypt_rtcp(data); } catch (Error e) { warning("%s (%d)", e.message, e.code); } -- cgit v1.2.3-70-g09d2 From fbc10c2023a4c2b874f87940f0a71bc0d8d7b57d Mon Sep 17 00:00:00 2001 From: Marvin W Date: Fri, 9 Apr 2021 22:23:53 +0200 Subject: DTLS-SRTP: Wait for setup finish and handle setup=passive --- plugins/crypto-vala/src/srtp.vala | 4 +- plugins/ice/src/dtls_srtp.vala | 162 ++++++++++++++------- plugins/ice/src/transport_parameters.vala | 65 +++++---- .../xep/0167_jingle_rtp/content_parameters.vala | 3 + .../0176_jingle_ice_udp/transport_parameters.vala | 16 +- 5 files changed, 159 insertions(+), 91 deletions(-) (limited to 'plugins/crypto-vala') diff --git a/plugins/crypto-vala/src/srtp.vala b/plugins/crypto-vala/src/srtp.vala index 77b5acde..493afdb0 100644 --- a/plugins/crypto-vala/src/srtp.vala +++ b/plugins/crypto-vala/src/srtp.vala @@ -6,8 +6,8 @@ public class Crypto.Srtp { public const string F8_128_HMAC_SHA1_80 = "F8_128_HMAC_SHA1_80"; public class Session { - public bool has_encrypt { get; private set; } - public bool has_decrypt { get; private set; } + public bool has_encrypt { get; private set; default = false; } + public bool has_decrypt { get; private set; default = false; } private Context encrypt_context; private Context decrypt_context; diff --git a/plugins/ice/src/dtls_srtp.vala b/plugins/ice/src/dtls_srtp.vala index e2470cf6..8a9b5dfa 100644 --- a/plugins/ice/src/dtls_srtp.vala +++ b/plugins/ice/src/dtls_srtp.vala @@ -1,9 +1,26 @@ using GnuTLS; -public class DtlsSrtp { +namespace Dino.Plugins.Ice.DtlsSrtp { + +public static Handler setup() throws GLib.Error { + var obj = new Handler(); + obj.generate_credentials(); + return obj; +} + +public class Handler { public signal void send_data(uint8[] data); + public bool ready { get { + return srtp_session.has_encrypt && srtp_session.has_decrypt; + }} + + public Mode mode { get; set; default = Mode.CLIENT; } + public uint8[] own_fingerprint { get; private set; } + public uint8[] peer_fingerprint { get; set; } + public string peer_fp_algo { get; set; } + private X509.Certificate[] own_cert; private X509.PrivateKey private_key; private Cond buffer_cond = new Cond(); @@ -11,27 +28,12 @@ public class DtlsSrtp { private Gee.LinkedList buffer_queue = new Gee.LinkedList(); private uint pull_timeout = uint.MAX; - private DigestAlgorithm? peer_fp_algo = null; - private uint8[] peer_fingerprint = null; - private uint8[] own_fingerprint; + private bool running = false; + private bool stop = false; + private bool restart = false; private Crypto.Srtp.Session srtp_session = new Crypto.Srtp.Session(); - public static DtlsSrtp setup() throws GLib.Error { - var obj = new DtlsSrtp(); - obj.generate_credentials(); - return obj; - } - - internal uint8[] get_own_fingerprint(DigestAlgorithm digest_algo) { - return own_fingerprint; - } - - public void set_peer_fingerprint(uint8[] fingerprint, DigestAlgorithm digest_algo) { - this.peer_fingerprint = fingerprint; - this.peer_fp_algo = digest_algo; - } - public uint8[] process_incoming_data(uint component_id, uint8[] data) { if (srtp_session.has_decrypt) { try { @@ -77,7 +79,7 @@ public class DtlsSrtp { buffer_mutex.unlock(); } - private void generate_credentials() throws GLib.Error { + internal void generate_credentials() throws GLib.Error { int err = 0; private_key = X509.PrivateKey.create(); @@ -102,8 +104,29 @@ public class DtlsSrtp { own_cert = new X509.Certificate[] { (owned)cert }; } - public async Xmpp.Xep.Jingle.ContentEncryption setup_dtls_connection(bool server) { - InitFlags server_or_client = server ? InitFlags.SERVER : InitFlags.CLIENT; + public void stop_dtls_connection() { + buffer_mutex.lock(); + stop = true; + buffer_cond.signal(); + buffer_mutex.unlock(); + } + + public async Xmpp.Xep.Jingle.ContentEncryption? setup_dtls_connection() { + buffer_mutex.lock(); + if (stop) { + restart = true; + buffer_mutex.unlock(); + return null; + } + if (running || ready) { + buffer_mutex.unlock(); + return null; + } + running = true; + restart = false; + buffer_mutex.unlock(); + + InitFlags server_or_client = mode == Mode.SERVER ? InitFlags.SERVER : InitFlags.CLIENT; debug("Setting up DTLS connection. We're %s", server_or_client.to_string()); CertificateCredentials cert_cred = CertificateCredentials.create(); @@ -131,7 +154,7 @@ public class DtlsSrtp { DateTime current_time = new DateTime.now_utc(); if (maximum_time.compare(current_time) < 0) { warning("DTLS handshake timeouted"); - return -1; + return ErrorCode.APPLICATION_ERROR_MIN + 1; } } while (err < 0 && !((ErrorCode)err).is_fatal()); Idle.add(setup_dtls_connection.callback); @@ -139,6 +162,17 @@ public class DtlsSrtp { }); yield; err = thread.join(); + buffer_mutex.lock(); + if (stop) { + stop = false; + running = false; + bool restart = restart; + buffer_mutex.unlock(); + if (restart) return yield setup_dtls_connection(); + return null; + } + buffer_mutex.unlock(); + throw_if_error(err); uint8[] km = new uint8[150]; Datum? client_key, client_salt, server_key, server_salt; @@ -147,7 +181,8 @@ public class DtlsSrtp { warning("SRTP client/server key/salt null"); } - if (server) { + debug("Finished DTLS connection. We're %s", server_or_client.to_string()); + if (mode == Mode.SERVER) { srtp_session.set_encryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, server_key.extract(), server_salt.extract()); srtp_session.set_decryption_key(Crypto.Srtp.AES_CM_128_HMAC_SHA1_80, client_key.extract(), client_salt.extract()); } else { @@ -158,24 +193,28 @@ public class DtlsSrtp { } private static ssize_t pull_function(void* transport_ptr, uint8[] buffer) { - DtlsSrtp self = transport_ptr as DtlsSrtp; + Handler self = transport_ptr as Handler; self.buffer_mutex.lock(); while (self.buffer_queue.size == 0) { self.buffer_cond.wait(self.buffer_mutex); + if (self.stop) { + self.buffer_mutex.unlock(); + return -1; + } } - owned Bytes data = self.buffer_queue.remove_at(0); + Bytes data = self.buffer_queue.remove_at(0); self.buffer_mutex.unlock(); - uint8[] data_uint8 = Bytes.unref_to_data(data); + uint8[] data_uint8 = Bytes.unref_to_data((owned) data); Memory.copy(buffer, data_uint8, data_uint8.length); // The callback should return 0 on connection termination, a positive number indicating the number of bytes received, and -1 on error. - return (ssize_t)data.length; + return (ssize_t)data_uint8.length; } private static int pull_timeout_function(void* transport_ptr, uint ms) { - DtlsSrtp self = transport_ptr as DtlsSrtp; + Handler self = transport_ptr as Handler; DateTime current_time = new DateTime.now_utc(); current_time.add_seconds(ms/1000); @@ -184,6 +223,10 @@ public class DtlsSrtp { self.buffer_mutex.lock(); while (self.buffer_queue.size == 0) { self.buffer_cond.wait_until(self.buffer_mutex, end_time); + if (self.stop) { + self.buffer_mutex.unlock(); + return -1; + } DateTime new_current_time = new DateTime.now_utc(); if (new_current_time.compare(current_time) > 0) { @@ -197,7 +240,7 @@ public class DtlsSrtp { } private static ssize_t push_function(void* transport_ptr, uint8[] buffer) { - DtlsSrtp self = transport_ptr as DtlsSrtp; + Handler self = transport_ptr as Handler; self.send_data(buffer); // The callback should return a positive number indicating the bytes sent, and -1 on error. @@ -205,7 +248,7 @@ public class DtlsSrtp { } private static int verify_function(Session session) { - DtlsSrtp self = session.get_transport_pointer() as DtlsSrtp; + Handler self = session.get_transport_pointer() as Handler; try { bool valid = self.verify_peer_cert(session); if (!valid) { @@ -232,7 +275,17 @@ public class DtlsSrtp { X509.Certificate peer_cert = X509.Certificate.create(); peer_cert.import(ref cert_datums[0], CertificateFormat.DER); - uint8[] real_peer_fp = get_fingerprint(peer_cert, peer_fp_algo); + DigestAlgorithm algo; + switch (peer_fp_algo) { + case "sha-256": + algo = DigestAlgorithm.SHA256; + break; + default: + warning("Unkown peer fingerprint algorithm: %s", peer_fp_algo); + return false; + } + + uint8[] real_peer_fp = get_fingerprint(peer_cert, algo); if (real_peer_fp.length != this.peer_fingerprint.length) { warning("Fingerprint lengths not equal %i vs %i", real_peer_fp.length, peer_fingerprint.length); @@ -248,27 +301,34 @@ public class DtlsSrtp { return true; } +} - private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) { - uint8[] buf = new uint8[512]; - size_t buf_out_size = 512; - certificate.get_fingerprint(digest_algo, buf, ref buf_out_size); +private uint8[] get_fingerprint(X509.Certificate certificate, DigestAlgorithm digest_algo) { + uint8[] buf = new uint8[512]; + size_t buf_out_size = 512; + certificate.get_fingerprint(digest_algo, buf, ref buf_out_size); - uint8[] ret = new uint8[buf_out_size]; - for (int i = 0; i < buf_out_size; i++) { - ret[i] = buf[i]; - } - return ret; + uint8[] ret = new uint8[buf_out_size]; + for (int i = 0; i < buf_out_size; i++) { + ret[i] = buf[i]; } - - private string format_fingerprint(uint8[] fingerprint) { - var sb = new StringBuilder(); - for (int i = 0; i < fingerprint.length; i++) { - sb.append("%02x".printf(fingerprint[i])); - if (i < fingerprint.length - 1) { - sb.append(":"); - } + return ret; +} + +private string format_fingerprint(uint8[] fingerprint) { + var sb = new StringBuilder(); + for (int i = 0; i < fingerprint.length; i++) { + sb.append("%02x".printf(fingerprint[i])); + if (i < fingerprint.length - 1) { + sb.append(":"); } - return sb.str; } -} \ No newline at end of file + return sb.str; +} + + +public enum Mode { + CLIENT, SERVER +} + +} diff --git a/plugins/ice/src/transport_parameters.vala b/plugins/ice/src/transport_parameters.vala index f95be261..e4862edc 100644 --- a/plugins/ice/src/transport_parameters.vala +++ b/plugins/ice/src/transport_parameters.vala @@ -9,11 +9,11 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport private bool we_want_connection; private bool remote_credentials_set; private Map connections = new HashMap(); - private DtlsSrtp? dtls_srtp; + private DtlsSrtp.Handler? dtls_srtp_handler; private class DatagramConnection : Jingle.DatagramConnection { private Nice.Agent agent; - private DtlsSrtp? dtls_srtp; + private DtlsSrtp.Handler? dtls_srtp_handler; private uint stream_id; private string? error; private ulong sent; @@ -22,9 +22,9 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport private ulong recv_reported; private ulong datagram_received_id; - public DatagramConnection(Nice.Agent agent, DtlsSrtp? dtls_srtp, uint stream_id, uint8 component_id) { + public DatagramConnection(Nice.Agent agent, DtlsSrtp.Handler? dtls_srtp_handler, uint stream_id, uint8 component_id) { this.agent = agent; - this.dtls_srtp = dtls_srtp; + this.dtls_srtp_handler = dtls_srtp_handler; this.stream_id = stream_id; this.component_id = component_id; this.datagram_received_id = this.datagram_received.connect((datagram) => { @@ -45,8 +45,8 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport public override void send_datagram(Bytes datagram) { if (this.agent != null && is_component_ready(agent, stream_id, component_id)) { uint8[] encrypted_data = null; - if (dtls_srtp != null) { - encrypted_data = dtls_srtp.process_outgoing_data(component_id, datagram.get_data()); + if (dtls_srtp_handler != null) { + encrypted_data = dtls_srtp_handler.process_outgoing_data(component_id, datagram.get_data()); if (encrypted_data == null) return; } agent.send(stream_id, component_id, encrypted_data ?? datagram.get_data()); @@ -65,13 +65,18 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport this.agent = agent; if (this.peer_fingerprint != null || !incoming) { - dtls_srtp = setup_dtls(this); - this.own_fingerprint = dtls_srtp.get_own_fingerprint(GnuTLS.DigestAlgorithm.SHA256); + dtls_srtp_handler = setup_dtls(this); + own_fingerprint = dtls_srtp_handler.own_fingerprint; if (incoming) { - dtls_srtp.set_peer_fingerprint(this.peer_fingerprint, this.peer_fp_algo == "sha-256" ? GnuTLS.DigestAlgorithm.SHA256 : GnuTLS.DigestAlgorithm.NULL); + own_setup = "active"; + dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT; + dtls_srtp_handler.peer_fingerprint = peer_fingerprint; + dtls_srtp_handler.peer_fp_algo = peer_fp_algo; } else { - dtls_srtp.setup_dtls_connection.begin(true, (_, res) => { - this.content.encryption = dtls_srtp.setup_dtls_connection.end(res); + own_setup = "actpass"; + dtls_srtp_handler.mode = DtlsSrtp.Mode.SERVER; + dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { + this.content.encryption = dtls_srtp_handler.setup_dtls_connection.end(res) ?? this.content.encryption; }); } } @@ -104,9 +109,9 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport agent.gather_candidates(stream_id); } - private static DtlsSrtp setup_dtls(TransportParameters tp) { + private static DtlsSrtp.Handler setup_dtls(TransportParameters tp) { var weak_self = new WeakRef(tp); - DtlsSrtp dtls_srtp = DtlsSrtp.setup(); + DtlsSrtp.Handler dtls_srtp = DtlsSrtp.setup(); dtls_srtp.send_data.connect((data) => { TransportParameters self = (TransportParameters) weak_self.get(); if (self != null) self.agent.send(self.stream_id, 1, data); @@ -144,10 +149,15 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport debug("on_transport_accept from %s", peer_full_jid.to_string()); base.handle_transport_accept(transport); - if (dtls_srtp != null && peer_fingerprint != null) { - dtls_srtp.set_peer_fingerprint(this.peer_fingerprint, this.peer_fp_algo == "sha-256" ? GnuTLS.DigestAlgorithm.SHA256 : GnuTLS.DigestAlgorithm.NULL); + if (dtls_srtp_handler != null && peer_fingerprint != null) { + dtls_srtp_handler.peer_fingerprint = peer_fingerprint; + dtls_srtp_handler.peer_fp_algo = peer_fp_algo; + if (peer_setup == "passive") { + dtls_srtp_handler.mode = DtlsSrtp.Mode.CLIENT; + dtls_srtp_handler.stop_dtls_connection(); + } } else { - dtls_srtp = null; + dtls_srtp_handler = null; } } @@ -200,18 +210,10 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport int new_candidates = agent.set_remote_candidates(stream_id, i, candidates); debug("Initiated component %u with %i remote candidates", i, new_candidates); - connections[i] = new DatagramConnection(agent, dtls_srtp, stream_id, i); + connections[i] = new DatagramConnection(agent, dtls_srtp_handler, stream_id, i); content.set_transport_connection(connections[i], i); } - if (incoming && dtls_srtp != null) { - Jingle.DatagramConnection rtp_datagram = (Jingle.DatagramConnection) content.get_transport_connection(1); - rtp_datagram.notify["ready"].connect(() => { - dtls_srtp.setup_dtls_connection.begin(false, (_, res) => { - this.content.encryption = dtls_srtp.setup_dtls_connection.end(res); - }); - }); - } base.create_transport_connection(stream, content); } @@ -219,11 +221,16 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport if (stream_id != this.stream_id) return; debug("stream %u component %u state changed to %s", stream_id, component_id, agent.get_component_state(stream_id, component_id).to_string()); may_consider_ready(stream_id, component_id); + if (incoming && dtls_srtp_handler != null && !dtls_srtp_handler.ready && is_component_ready(agent, stream_id, component_id) && dtls_srtp_handler.mode == DtlsSrtp.Mode.CLIENT) { + dtls_srtp_handler.setup_dtls_connection.begin((_, res) => { + this.content.encryption = dtls_srtp_handler.setup_dtls_connection.end(res) ?? this.content.encryption; + }); + } } private void may_consider_ready(uint stream_id, uint component_id) { if (stream_id != this.stream_id) return; - if (connections.has_key((uint8) component_id) && is_component_ready(agent, stream_id, component_id) && connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready) { + if (connections.has_key((uint8) component_id) && !connections[(uint8)component_id].ready && is_component_ready(agent, stream_id, component_id) && (dtls_srtp_handler == null || dtls_srtp_handler.ready)) { connections[(uint8)component_id].ready = true; } } @@ -241,8 +248,8 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport private void on_recv(Nice.Agent agent, uint stream_id, uint component_id, uint8[] data) { if (stream_id != this.stream_id) return; uint8[] decrypt_data = null; - if (dtls_srtp != null) { - decrypt_data = dtls_srtp.process_incoming_data(component_id, data); + if (dtls_srtp_handler != null) { + decrypt_data = dtls_srtp_handler.process_incoming_data(component_id, data); if (decrypt_data == null) return; } may_consider_ready(stream_id, component_id); @@ -317,4 +324,4 @@ public class Dino.Plugins.Ice.TransportParameters : JingleIceUdp.IceUdpTransport return candidate; } -} \ No newline at end of file +} diff --git a/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala b/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala index ac65f88c..c37c19cc 100644 --- a/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala +++ b/xmpp-vala/src/module/xep/0167_jingle_rtp/content_parameters.vala @@ -94,6 +94,9 @@ public class Xmpp.Xep.JingleRtp.Parameters : Jingle.ContentParameters, Object { ulong rtp_ready_handler_id = 0; rtp_ready_handler_id = rtp_datagram.notify["ready"].connect(() => { this.stream.on_rtp_ready(); + if (rtcp_mux) { + this.stream.on_rtcp_ready(); + } connection_ready(); rtp_datagram.disconnect(rtp_ready_handler_id); diff --git a/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala b/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala index f194f06d..4976f560 100644 --- a/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala +++ b/xmpp-vala/src/module/xep/0176_jingle_ice_udp/transport_parameters.vala @@ -14,8 +14,10 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T public Gee.List remote_candidates = new ArrayList(Candidate.equals_func); public uint8[]? own_fingerprint = null; + public string? own_setup = null; public uint8[]? peer_fingerprint = null; public string? peer_fp_algo = null; + public string? peer_setup = null; public Jid local_full_jid { get; private set; } public Jid peer_full_jid { get; private set; } @@ -41,8 +43,9 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T StanzaNode? fingerprint_node = node.get_subnode("fingerprint", DTLS_NS_URI); if (fingerprint_node != null) { - peer_fingerprint = fingerprint_to_bytes(fingerprint_node.get_deep_string_content()); + peer_fingerprint = fingerprint_to_bytes(fingerprint_node.get_string_content()); peer_fp_algo = fingerprint_node.get_attribute("hash"); + peer_setup = fingerprint_node.get_attribute("setup"); } } } @@ -73,11 +76,7 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T .add_self_xmlns() .put_attribute("hash", "sha-256") .put_node(new StanzaNode.text(format_fingerprint(own_fingerprint))); - if (incoming) { - fingerprint_node.put_attribute("setup", "active"); - } else { - fingerprint_node.put_attribute("setup", "actpass"); - } + fingerprint_node.put_attribute("setup", own_setup); node.put_node(fingerprint_node); } @@ -101,6 +100,7 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T if (fingerprint_node != null) { peer_fingerprint = fingerprint_to_bytes(fingerprint_node.get_deep_string_content()); peer_fp_algo = fingerprint_node.get_attribute("hash"); + peer_setup = fingerprint_node.get_attribute("setup"); } } @@ -142,8 +142,6 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T } } - - private string format_fingerprint(uint8[] fingerprint) { var sb = new StringBuilder(); for (int i = 0; i < fingerprint.length; i++) { @@ -167,4 +165,4 @@ public abstract class Xmpp.Xep.JingleIceUdp.IceUdpTransportParameters : Jingle.T } return bin; } -} \ No newline at end of file +} -- cgit v1.2.3-70-g09d2 From 4edab3c8d63b327dcb48799e174a3e00192721ec Mon Sep 17 00:00:00 2001 From: Marvin W Date: Sun, 11 Apr 2021 15:12:53 +0200 Subject: Fix custom vapi integration --- plugins/crypto-vala/CMakeLists.txt | 6 ++---- plugins/ice/CMakeLists.txt | 10 +++++----- plugins/omemo/CMakeLists.txt | 7 +++---- plugins/rtp/CMakeLists.txt | 2 -- 4 files changed, 10 insertions(+), 15 deletions(-) (limited to 'plugins/crypto-vala') diff --git a/plugins/crypto-vala/CMakeLists.txt b/plugins/crypto-vala/CMakeLists.txt index 4a8da241..f615854c 100644 --- a/plugins/crypto-vala/CMakeLists.txt +++ b/plugins/crypto-vala/CMakeLists.txt @@ -1,9 +1,9 @@ find_package(GCrypt REQUIRED) +find_package(Srtp2 REQUIRED) find_packages(CRYPTO_VALA_PACKAGES REQUIRED GLib GObject GIO - Srtp2 ) vala_precompile(CRYPTO_VALA_C @@ -18,8 +18,6 @@ CUSTOM_VAPIS "${CMAKE_CURRENT_SOURCE_DIR}/vapi/libsrtp2.vapi" PACKAGES ${CRYPTO_VALA_PACKAGES} -OPTIONS - --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi GENERATE_VAPI crypto-vala GENERATE_HEADER @@ -29,6 +27,6 @@ GENERATE_HEADER set(CFLAGS ${VALA_CFLAGS}) add_definitions(${CFLAGS}) add_library(crypto-vala STATIC ${CRYPTO_VALA_C}) -target_link_libraries(crypto-vala ${CRYPTO_VALA_PACKAGES} gcrypt) +target_link_libraries(crypto-vala ${CRYPTO_VALA_PACKAGES} gcrypt libsrtp2) set_property(TARGET crypto-vala PROPERTY POSITION_INDEPENDENT_CODE ON) diff --git a/plugins/ice/CMakeLists.txt b/plugins/ice/CMakeLists.txt index 392a202f..b18bfcc5 100644 --- a/plugins/ice/CMakeLists.txt +++ b/plugins/ice/CMakeLists.txt @@ -1,11 +1,11 @@ +find_package(Nice REQUIRED) +find_package(GnuTLS REQUIRED) find_packages(ICE_PACKAGES REQUIRED Gee GLib GModule - GnuTLS GObject GTK3 - Nice ) vala_precompile(ICE_VALA_C @@ -21,15 +21,15 @@ CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/dino.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/crypto-vala.vapi + ${CMAKE_CURRENT_SOURCE_DIR}/vapi/nice.vapi + ${CMAKE_CURRENT_SOURCE_DIR}/vapi/gnutls.vapi PACKAGES ${ICE_PACKAGES} -OPTIONS - --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="ice") add_library(ice SHARED ${ICE_VALA_C}) -target_link_libraries(ice libdino crypto-vala ${ICE_PACKAGES}) +target_link_libraries(ice libdino crypto-vala ${ICE_PACKAGES} nice gnutls) set_target_properties(ice PROPERTIES PREFIX "") set_target_properties(ice PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) diff --git a/plugins/omemo/CMakeLists.txt b/plugins/omemo/CMakeLists.txt index 0f5a1521..c7a45069 100644 --- a/plugins/omemo/CMakeLists.txt +++ b/plugins/omemo/CMakeLists.txt @@ -3,13 +3,13 @@ find_package(Gettext) include(${GETTEXT_USE_FILE}) gettext_compile(${GETTEXT_PACKAGE} SOURCE_DIR ${CMAKE_CURRENT_SOURCE_DIR}/po TARGET_NAME ${GETTEXT_PACKAGE}-translations) +find_package(Qrencode REQUIRED) find_packages(OMEMO_PACKAGES REQUIRED Gee GLib GModule GObject GTK3 - Qrencode ) set(RESOURCE_LIST @@ -66,18 +66,17 @@ CUSTOM_VAPIS ${CMAKE_BINARY_DIR}/exports/xmpp-vala.vapi ${CMAKE_BINARY_DIR}/exports/qlite.vapi ${CMAKE_BINARY_DIR}/exports/dino.vapi + ${CMAKE_CURRENT_SOURCE_DIR}/vapi/libqrencode.vapi PACKAGES ${OMEMO_PACKAGES} GRESOURCES ${OMEMO_GRESOURCES_XML} -OPTIONS - --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi ) add_definitions(${VALA_CFLAGS} -DGETTEXT_PACKAGE=\"${GETTEXT_PACKAGE}\" -DLOCALE_INSTALL_DIR=\"${LOCALE_INSTALL_DIR}\" -DG_LOG_DOMAIN="OMEMO") add_library(omemo SHARED ${OMEMO_VALA_C} ${OMEMO_GRESOURCES_TARGET}) add_dependencies(omemo ${GETTEXT_PACKAGE}-translations) -target_link_libraries(omemo libdino signal-protocol-vala crypto-vala ${OMEMO_PACKAGES}) +target_link_libraries(omemo libdino signal-protocol-vala crypto-vala ${OMEMO_PACKAGES} libqrencode) set_target_properties(omemo PROPERTIES PREFIX "") set_target_properties(omemo PROPERTIES LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/plugins/) diff --git a/plugins/rtp/CMakeLists.txt b/plugins/rtp/CMakeLists.txt index 0925ff0c..76d6e66d 100644 --- a/plugins/rtp/CMakeLists.txt +++ b/plugins/rtp/CMakeLists.txt @@ -31,8 +31,6 @@ PACKAGES ${RTP_PACKAGES} DEFINITIONS ${RTP_DEFINITIONS} -OPTIONS - --vapidir=${CMAKE_CURRENT_SOURCE_DIR}/vapi ) add_definitions(${VALA_CFLAGS} -DG_LOG_DOMAIN="rtp" -I${CMAKE_CURRENT_SOURCE_DIR}/src) -- cgit v1.2.3-70-g09d2