/* Utility */ PJ_DEF(pj_status_t) pjmedia_transport_srtp_decrypt_pkt(pjmedia_transport *tp, pj_bool_t is_rtp, void *pkt, int *pkt_len) { transport_srtp *srtp = (transport_srtp *)tp; err_status_t err; if (srtp->bypass_srtp) return PJ_SUCCESS; PJ_ASSERT_RETURN(tp && pkt && (*pkt_len>0), PJ_EINVAL); PJ_ASSERT_RETURN(srtp->session_inited, PJ_EINVALIDOP); /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((long)pkt) & 0x03)==0, return PJ_EINVAL); pj_lock_acquire(srtp->mutex); if (is_rtp) err = srtp_unprotect(srtp->srtp_rx_ctx, pkt, pkt_len); else err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, pkt, pkt_len); if (err != err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", *pkt_len, get_libsrtp_errstr(err))); } pj_lock_release(srtp->mutex); return (err==err_status_ok) ? PJ_SUCCESS : PJMEDIA_ERRNO_FROM_LIBSRTP(err); }
/* * This callback is called by transport when incoming rtcp is received */ static void srtp_rtcp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = size; err_status_t err; if (srtp->bypass_srtp) { srtp->rtcp_cb(srtp->user_data, pkt, size); return; } if (size < 0 || !srtp->session_inited) { return; } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((long)pkt) & 0x03)==0, return ); pj_lock_acquire(srtp->mutex); err = srtp_unprotect_rtcp(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (err == err_status_ok) { srtp->rtcp_cb(srtp->user_data, pkt, len); } else { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTCP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } pj_lock_release(srtp->mutex); }
static bool recv_handler(struct sa *src, struct mbuf *mb, void *arg) { struct menc_st *st = arg; err_status_t e; int len; (void)src; if (!st->use_srtp || !is_rtp_or_rtcp(mb)) return false; len = (int)mbuf_get_left(mb); if (is_rtcp_packet(mb)) { e = srtp_unprotect_rtcp(st->srtp_rx, mbuf_buf(mb), &len); } else { e = srtp_unprotect(st->srtp_rx, mbuf_buf(mb), &len); } if (e != err_status_ok) { DEBUG_WARNING("recv: failed to unprotect %s-packet" " with %d bytes (%H)\n", is_rtcp_packet(mb) ? "RTCP" : "RTP", len, errstatus_print, e); return true; /* error - drop packet */ } mbuf_set_end(mb, mb->pos + len); return false; /* continue processing */ }
static int srtcp_recvfrom(RtpTransport *t, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen){ srtp_t srtp=(srtp_t)t->data; int err; int slen; err=rtp_session_rtp_recv_abstract(t->session->rtcp.socket,m,flags,from,fromlen); if (err>0){ err_status_t srtp_err; /* keep NON-RTP data unencrypted */ rtcp_common_header_t *rtcp; if (err>=RTCP_COMMON_HEADER_SIZE) { rtcp = (rtcp_common_header_t*)m->b_wptr; if (rtcp->version!=2) { return err; } } slen=err; srtp_err=srtp_unprotect_rtcp(srtp,m->b_wptr,&slen); if (srtp_err==err_status_ok) return slen; else { ortp_error("srtp_unprotect_rtcp() failed (%d)", srtp_err); return -1; } } return err; }
int SrtpChannel::unprotectRtcp(char* buffer, int *len) { if (!active_) return 0; int val = srtp_unprotect_rtcp(receive_session_, buffer, len); if (val == 0) { return 0; } else { ELOG_WARN("Error SrtpChannel::unprotectRtcp %u", val); return -1; } }
srtpw_err_status_t srtpw_srtp_unprotect(srtpw_srtp *srtp, void *buf, int *len, int rtcp) { int res; if ((res = rtcp ? srtp_unprotect_rtcp((srtp_t)srtp, buf, len) : srtp_unprotect((srtp_t)srtp, buf, len)) != err_status_ok && res != err_status_replay_fail) { srtpw_log(err_level_debug, "SRTP unprotect: failed :%d \n", res); return -1; } return *len; }
static int srtcp_recvfrom(RtpTransport *t, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen){ srtp_t srtp=(srtp_t)t->data; int err; int slen; err=recvfrom(t->session->rtcp.socket,m->b_wptr,m->b_datap->db_lim-m->b_datap->db_base,flags,from,fromlen); if (err>0){ slen=err; if (srtp_unprotect_rtcp(srtp,m->b_wptr,&slen)==err_status_ok) return slen; else { ortp_error("srtp_unprotect_rtcp() failed"); return -1; } } return err; }
static int srtcp_recvfrom(RtpTransport *t, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen){ srtp_t srtp=(srtp_t)t->data; int err; int slen; err=rtp_session_rtp_recv_abstract(t->session->rtcp.socket,m,flags,from,fromlen); if (err>0){ err_status_t srtp_err; slen=err; srtp_err=srtp_unprotect_rtcp(srtp,m->b_wptr,&slen); if (srtp_err==err_status_ok) return slen; else { ortp_error("srtp_unprotect_rtcp() failed (%d)", srtp_err); return -1; } } return err; }
static int ozrtp_rtcp_recvfrom(RtpTransport *t, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen){ ZrtpContext *zrtpContext = (ZrtpContext*) t->data; OrtpZrtpContext *userData = (OrtpZrtpContext*) zrtpContext->userData; int rlen = rtp_session_rtp_recv_abstract(t->session->rtcp.socket,m,flags,from,fromlen); if (rlen<=0) { // nothing was received or error: pass the information to caller return rlen; } if (userData->srtpRecv != NULL && zrtp_inState(zrtpContext, SecureState)) { err_status_t err = srtp_unprotect_rtcp(userData->srtpRecv,m->b_wptr,&rlen); if (err != err_status_ok) { ortp_error("srtp_unprotect failed %d ; packet discarded (may be plain RTCP)", err); return 0; } } return rlen; }
nsresult SrtpFlow::UnprotectRtcp(void *in, int in_len, int max_len, int *out_len) { nsresult res = CheckInputs(false, in, in_len, max_len, out_len); if (NS_FAILED(res)) return res; int len = in_len; err_status_t r = srtp_unprotect_rtcp(session_, in, &len); if (r != err_status_ok) { MOZ_MTLOG(PR_LOG_ERROR, "Error unprotecting SRTCP packet"); return NS_ERROR_FAILURE; } MOZ_ASSERT(len <= max_len); *out_len = len; MOZ_MTLOG(PR_LOG_DEBUG, "Successfully unprotected an SRTCP packet of len " << *out_len); return NS_OK; }
nsresult SrtpFlow::UnprotectRtcp(void *in, int in_len, int max_len, int *out_len) { nsresult res = CheckInputs(false, in, in_len, max_len, out_len); if (NS_FAILED(res)) return res; int len = in_len; srtp_err_status_t r = srtp_unprotect_rtcp(session_, in, &len); if (r != srtp_err_status_ok) { CSFLogError(LOGTAG, "Error unprotecting SRTCP packet error=%d", (int)r); return NS_ERROR_FAILURE; } MOZ_ASSERT(len <= max_len); *out_len = len; CSFLogDebug(LOGTAG, "Successfully unprotected an SRTCP packet of len %d", *out_len); return NS_OK; }
/* * This function should be called while holding the filter lock */ static gboolean gst_srtp_dec_decode_buffer (GstSrtpDec * filter, GstPad * pad, GstBuffer * buf, gboolean is_rtcp, guint32 ssrc) { GstMapInfo map; err_status_t err; gint size; GST_LOG_OBJECT (pad, "Received %s buffer of size %" G_GSIZE_FORMAT " with SSRC = %u", is_rtcp ? "RTCP" : "RTP", gst_buffer_get_size (buf), ssrc); /* Change buffer to remove protection */ buf = gst_buffer_make_writable (buf); gst_buffer_map (buf, &map, GST_MAP_READWRITE); size = map.size; unprotect: gst_srtp_init_event_reporter (); if (is_rtcp) err = srtp_unprotect_rtcp (filter->session, map.data, &size); else { /* If ROC has changed, we know we need to set the initial RTP * sequence number too. */ if (filter->roc_changed) { srtp_stream_t stream; stream = srtp_get_stream (filter->session, htonl (ssrc)); if (stream) { guint16 seqnum = 0; GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT; gst_rtp_buffer_map (buf, GST_MAP_READ | GST_RTP_BUFFER_MAP_FLAG_SKIP_PADDING, &rtpbuf); seqnum = gst_rtp_buffer_get_seq (&rtpbuf); gst_rtp_buffer_unmap (&rtpbuf); /* We finally add the RTP sequence number to the current * rollover counter. */ stream->rtp_rdbx.index &= ~0xFFFF; stream->rtp_rdbx.index |= seqnum; } filter->roc_changed = FALSE; } err = srtp_unprotect (filter->session, map.data, &size); } GST_OBJECT_UNLOCK (filter); if (err != err_status_ok) { GST_WARNING_OBJECT (pad, "Unable to unprotect buffer (unprotect failed code %d)", err); /* Signal user depending on type of error */ switch (err) { case err_status_key_expired: GST_OBJECT_LOCK (filter); /* Update stream */ if (find_stream_by_ssrc (filter, ssrc)) { GST_OBJECT_UNLOCK (filter); if (request_key_with_signal (filter, ssrc, SIGNAL_HARD_LIMIT)) { GST_OBJECT_LOCK (filter); goto unprotect; } else { GST_WARNING_OBJECT (filter, "Hard limit reached, no new key, " "dropping"); } } else { GST_WARNING_OBJECT (filter, "Could not find matching stream, " "dropping"); } break; case err_status_auth_fail: GST_WARNING_OBJECT (filter, "Error authentication packet, dropping"); break; case err_status_cipher_fail: GST_WARNING_OBJECT (filter, "Error while decrypting packet, dropping"); break; default: GST_WARNING_OBJECT (filter, "Other error, dropping"); break; } gst_buffer_unmap (buf, &map); GST_OBJECT_LOCK (filter); return FALSE; } gst_buffer_unmap (buf, &map); gst_buffer_set_size (buf, size); GST_OBJECT_LOCK (filter); return TRUE; }
err_status_t srtcp_test(const srtp_policy_t *policy) { int i; srtp_t srtcp_sender; srtp_t srtcp_rcvr; err_status_t status = err_status_ok; srtp_hdr_t *hdr, *hdr2; uint8_t hdr_enc[64]; uint8_t *pkt_end; int msg_len_octets, msg_len_enc; int len; int tag_length = policy->rtp.auth_tag_len; uint32_t ssrc; srtp_policy_t *rcvr_policy; err_check(srtp_create(&srtcp_sender, policy)); /* print out policy */ err_check(srtp_session_print_policy(srtcp_sender)); /* * initialize data buffer, using the ssrc in the policy unless that * value is a wildcard, in which case we'll just use an arbitrary * one */ if (policy->ssrc.type != ssrc_specific) ssrc = 0xdecafbad; else ssrc = policy->ssrc.value; msg_len_octets = 28; hdr = srtp_create_test_packet(msg_len_octets, ssrc); if (hdr == NULL) return err_status_alloc_fail; hdr2 = srtp_create_test_packet(msg_len_octets, ssrc); if (hdr2 == NULL) { free(hdr); return err_status_alloc_fail; } /* set message length */ len = msg_len_octets; debug_print(mod_driver, "before protection:\n%s", srtp_packet_to_string(hdr, len)); #if PRINT_REFERENCE_PACKET debug_print(mod_driver, "reference packet before protection:\n%s", octet_string_hex_string((uint8_t *)hdr, len)); #endif err_check(srtp_protect_rtcp(srtcp_sender, hdr, &len)); debug_print(mod_driver, "after protection:\n%s", srtp_packet_to_string(hdr, len)); #if PRINT_REFERENCE_PACKET debug_print(mod_driver, "after protection:\n%s", octet_string_hex_string((uint8_t *)hdr, len)); #endif /* save protected message and length */ memcpy(hdr_enc, hdr, len); msg_len_enc = len; /* * check for overrun of the srtp_protect() function * * The packet is followed by a value of 0xfffff; if the value of the * data following the packet is different, then we know that the * protect function is overwriting the end of the packet. */ pkt_end = (uint8_t *)hdr + sizeof(srtp_hdr_t) + msg_len_octets + tag_length; for (i = 0; i < 4; i++) if (pkt_end[i] != 0xff) { fprintf(stdout, "overwrite in srtp_protect_rtcp() function " "(expected %x, found %x in trailing octet %d)\n", 0xff, ((uint8_t *)hdr)[i], i); free(hdr); free(hdr2); return err_status_algo_fail; } /* * if the policy includes confidentiality, check that ciphertext is * different than plaintext * * Note that this check will give false negatives, with some small * probability, especially if the packets are short. For that * reason, we skip this check if the plaintext is less than four * octets long. */ if ((policy->rtp.sec_serv & sec_serv_conf) && (msg_len_octets >= 4)) { printf("testing that ciphertext is distinct from plaintext..."); status = err_status_algo_fail; for (i=12; i < msg_len_octets+12; i++) if (((uint8_t *)hdr)[i] != ((uint8_t *)hdr2)[i]) { status = err_status_ok; } if (status) { printf("failed\n"); free(hdr); free(hdr2); return status; } printf("passed\n"); } /* * if the policy uses a 'wildcard' ssrc, then we need to make a copy * of the policy that changes the direction to inbound * * we always copy the policy into the rcvr_policy, since otherwise * the compiler would fret about the constness of the policy */ rcvr_policy = malloc(sizeof(srtp_policy_t)); if (rcvr_policy == NULL) return err_status_alloc_fail; memcpy(rcvr_policy, policy, sizeof(srtp_policy_t)); if (policy->ssrc.type == ssrc_any_outbound) { rcvr_policy->ssrc.type = ssrc_any_inbound; } err_check(srtp_create(&srtcp_rcvr, rcvr_policy)); err_check(srtp_unprotect_rtcp(srtcp_rcvr, hdr, &len)); debug_print(mod_driver, "after unprotection:\n%s", srtp_packet_to_string(hdr, len)); /* verify that the unprotected packet matches the origial one */ for (i=0; i < msg_len_octets; i++) if (((uint8_t *)hdr)[i] != ((uint8_t *)hdr2)[i]) { fprintf(stdout, "mismatch at octet %d\n", i); status = err_status_algo_fail; } if (status) { free(hdr); free(hdr2); return status; } /* * if the policy includes authentication, then test for false positives */ if (policy->rtp.sec_serv & sec_serv_auth) { char *data = ((char *)hdr) + 12; printf("testing for false positives in replay check..."); /* set message length */ len = msg_len_enc; /* unprotect a second time - should fail with a replay error */ status = srtp_unprotect_rtcp(srtcp_rcvr, hdr_enc, &len); if (status != err_status_replay_fail) { printf("failed with error code %d\n", status); free(hdr); free(hdr2); return status; } else { printf("passed\n"); } printf("testing for false positives in auth check..."); /* increment sequence number in header */ hdr->seq++; /* set message length */ len = msg_len_octets; /* apply protection */ err_check(srtp_protect_rtcp(srtcp_sender, hdr, &len)); /* flip bits in packet */ data[0] ^= 0xff; /* unprotect, and check for authentication failure */ status = srtp_unprotect_rtcp(srtcp_rcvr, hdr, &len); if (status != err_status_auth_fail) { printf("failed\n"); free(hdr); free(hdr2); return status; } else { printf("passed\n"); } } err_check(srtp_dealloc(srtcp_sender)); err_check(srtp_dealloc(srtcp_rcvr)); free(hdr); free(hdr2); return err_status_ok; }
static GstFlowReturn gst_srtp_dec_chain (GstPad * pad, GstObject * parent, GstBuffer * buf, gboolean is_rtcp) { GstSrtpDec *filter = GST_SRTP_DEC (parent); GstPad *otherpad; err_status_t err = err_status_ok; GstSrtpDecSsrcStream *stream = NULL; GstFlowReturn ret = GST_FLOW_OK; gint size; guint32 ssrc = 0; GstMapInfo map; GST_OBJECT_LOCK (filter); /* Check if this stream exists, if not create a new stream */ if (!(stream = validate_buffer (filter, buf, &ssrc, &is_rtcp))) { GST_OBJECT_UNLOCK (filter); GST_WARNING_OBJECT (filter, "Invalid buffer, dropping"); goto drop_buffer; } if (!STREAM_HAS_CRYPTO (stream)) { GST_OBJECT_UNLOCK (filter); goto push_out; } GST_LOG_OBJECT (pad, "Received %s buffer of size %" G_GSIZE_FORMAT " with SSRC = %u", is_rtcp ? "RTCP" : "RTP", gst_buffer_get_size (buf), ssrc); /* Change buffer to remove protection */ buf = gst_buffer_make_writable (buf); unprotect: gst_buffer_map (buf, &map, GST_MAP_READWRITE); size = map.size; gst_srtp_init_event_reporter (); if (is_rtcp) err = srtp_unprotect_rtcp (filter->session, map.data, &size); else { /* If ROC has changed, we know we need to set the initial RTP * sequence number too. */ if (filter->roc_changed) { srtp_stream_t stream; stream = srtp_get_stream (filter->session, htonl (ssrc)); if (stream) { guint16 seqnum = 0; GstRTPBuffer rtpbuf = GST_RTP_BUFFER_INIT; gst_rtp_buffer_map (buf, GST_MAP_READ, &rtpbuf); seqnum = gst_rtp_buffer_get_seq (&rtpbuf); gst_rtp_buffer_unmap (&rtpbuf); /* We finally add the RTP sequence number to the current * rollover counter. */ stream->rtp_rdbx.index &= ~0xFFFF; stream->rtp_rdbx.index |= seqnum; } filter->roc_changed = FALSE; } err = srtp_unprotect (filter->session, map.data, &size); } gst_buffer_unmap (buf, &map); GST_OBJECT_UNLOCK (filter); if (err != err_status_ok) { GST_WARNING_OBJECT (pad, "Unable to unprotect buffer (unprotect failed code %d)", err); /* Signal user depending on type of error */ switch (err) { case err_status_key_expired: GST_OBJECT_LOCK (filter); /* Update stream */ if (find_stream_by_ssrc (filter, ssrc)) { GST_OBJECT_UNLOCK (filter); if (request_key_with_signal (filter, ssrc, SIGNAL_HARD_LIMIT)) { GST_OBJECT_LOCK (filter); goto unprotect; } else { GST_WARNING_OBJECT (filter, "Hard limit reached, no new key, " "dropping"); } } else { GST_WARNING_OBJECT (filter, "Could not find matching stream, " "dropping"); } break; case err_status_auth_fail: GST_WARNING_OBJECT (filter, "Error authentication packet, dropping"); break; case err_status_cipher_fail: GST_WARNING_OBJECT (filter, "Error while decrypting packet, dropping"); break; default: GST_WARNING_OBJECT (filter, "Other error, dropping"); break; } goto drop_buffer; } gst_buffer_set_size (buf, size); /* If all is well, we may have reached soft limit */ if (gst_srtp_get_soft_limit_reached ()) request_key_with_signal (filter, ssrc, SIGNAL_SOFT_LIMIT); push_out: /* Push buffer to source pad */ if (is_rtcp) { otherpad = filter->rtcp_srcpad; if (!filter->rtcp_has_segment) gst_srtp_dec_push_early_events (filter, filter->rtcp_srcpad, filter->rtp_srcpad, TRUE); } else { otherpad = filter->rtp_srcpad; if (!filter->rtp_has_segment) gst_srtp_dec_push_early_events (filter, filter->rtp_srcpad, filter->rtcp_srcpad, FALSE); } ret = gst_pad_push (otherpad, buf); return ret; drop_buffer: /* Drop buffer, except if gst_pad_push returned OK or an error */ gst_buffer_unref (buf); return ret; }
srtp_err_status_t test_dtls_srtp(void) { srtp_hdr_t *test_packet; int test_packet_len = 80; srtp_t s; srtp_policy_t policy; uint8_t key[SRTP_MAX_KEY_LEN]; uint8_t salt[SRTP_MAX_KEY_LEN]; unsigned int key_len, salt_len; srtp_profile_t profile; srtp_err_status_t err; /* create a 'null' SRTP session */ err = srtp_create(&s, NULL); if (err) return err; /* * verify that packet-processing functions behave properly - we * expect that these functions will return srtp_err_status_no_ctx */ test_packet = srtp_create_test_packet(80, 0xa5a5a5a5); if (test_packet == NULL) return srtp_err_status_alloc_fail; err = srtp_protect(s, test_packet, &test_packet_len); if (err != srtp_err_status_no_ctx) { printf("wrong return value from srtp_protect() (got code %d)\n", err); return srtp_err_status_fail; } err = srtp_unprotect(s, test_packet, &test_packet_len); if (err != srtp_err_status_no_ctx) { printf("wrong return value from srtp_unprotect() (got code %d)\n", err); return srtp_err_status_fail; } err = srtp_protect_rtcp(s, test_packet, &test_packet_len); if (err != srtp_err_status_no_ctx) { printf("wrong return value from srtp_protect_rtcp() (got code %d)\n", err); return srtp_err_status_fail; } err = srtp_unprotect_rtcp(s, test_packet, &test_packet_len); if (err != srtp_err_status_no_ctx) { printf("wrong return value from srtp_unprotect_rtcp() (got code %d)\n", err); return srtp_err_status_fail; } /* * set keys to known values for testing */ profile = srtp_profile_aes128_cm_sha1_80; key_len = srtp_profile_get_master_key_length(profile); salt_len = srtp_profile_get_master_salt_length(profile); memset(key, 0xff, key_len); memset(salt, 0xee, salt_len); srtp_append_salt_to_key(key, key_len, salt, salt_len); policy.key = key; /* initialize SRTP policy from profile */ err = srtp_crypto_policy_set_from_profile_for_rtp(&policy.rtp, profile); if (err) return err; err = srtp_crypto_policy_set_from_profile_for_rtcp(&policy.rtcp, profile); if (err) return err; policy.ssrc.type = ssrc_any_inbound; policy.ekt = NULL; policy.window_size = 128; policy.allow_repeat_tx = 0; policy.next = NULL; err = srtp_add_stream(s, &policy); if (err) return err; err = srtp_dealloc(s); if (err) return err; free(test_packet); return srtp_err_status_ok; }