err_status_t srtp_validate() { unsigned char test_key[30] = { 0xe1, 0xf9, 0x7a, 0x0d, 0x3e, 0x01, 0x8b, 0xe0, 0xd6, 0x4f, 0xa3, 0x2c, 0x06, 0xde, 0x41, 0x39, 0x0e, 0xc6, 0x75, 0xad, 0x49, 0x8a, 0xfe, 0xeb, 0xb6, 0x96, 0x0b, 0x3a, 0xab, 0xe6 }; uint8_t srtp_plaintext_ref[28] = { 0x80, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad, 0xca, 0xfe, 0xba, 0xbe, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab }; uint8_t srtp_plaintext[38] = { 0x80, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad, 0xca, 0xfe, 0xba, 0xbe, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0xab, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; uint8_t srtp_ciphertext[38] = { 0x80, 0x0f, 0x12, 0x34, 0xde, 0xca, 0xfb, 0xad, 0xca, 0xfe, 0xba, 0xbe, 0x4e, 0x55, 0xdc, 0x4c, 0xe7, 0x99, 0x78, 0xd8, 0x8c, 0xa4, 0xd2, 0x15, 0x94, 0x9d, 0x24, 0x02, 0xb7, 0x8d, 0x6a, 0xcc, 0x99, 0xea, 0x17, 0x9b, 0x8d, 0xbb }; srtp_t srtp_snd, srtp_recv; err_status_t status; int len; srtp_policy_t policy; /* * create a session with a single stream using the default srtp * policy and with the SSRC value 0xcafebabe */ crypto_policy_set_rtp_default(&policy.rtp); crypto_policy_set_rtcp_default(&policy.rtcp); policy.ssrc.type = ssrc_specific; policy.ssrc.value = 0xcafebabe; policy.key = test_key; policy.next = NULL; status = srtp_create(&srtp_snd, &policy); if (status) return status; /* * protect plaintext, then compare with ciphertext */ len = 28; status = srtp_protect(srtp_snd, srtp_plaintext, &len); if (status || (len != 38)) return err_status_fail; debug_print(mod_driver, "ciphertext:\n %s", octet_string_hex_string(srtp_plaintext, len)); debug_print(mod_driver, "ciphertext reference:\n %s", octet_string_hex_string(srtp_ciphertext, len)); if (octet_string_is_eq(srtp_plaintext, srtp_ciphertext, len)) return err_status_fail; /* * create a receiver session context comparable to the one created * above - we need to do this so that the replay checking doesn't * complain */ status = srtp_create(&srtp_recv, &policy); if (status) return status; /* * unprotect ciphertext, then compare with plaintext */ status = srtp_unprotect(srtp_recv, srtp_ciphertext, &len); if (status || (len != 28)) return status; if (octet_string_is_eq(srtp_ciphertext, srtp_plaintext_ref, len)) return err_status_fail; return err_status_ok; }
/* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_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->rtp_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 ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == err_status_replay_old || err == err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will returning err_status_replay_*. Restarting SRTP can resolve * this. */ if (pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &srtp->tx_policy, &srtp->rx_policy) != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err == err_status_ok) { srtp->rtp_cb(srtp->user_data, pkt, len); } else { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } pj_lock_release(srtp->mutex); }
unsigned int rtp_recvfrom(rtp_receiver_t receiver, void *msg, int *len) { int octets_recvd; err_status_t stat; octets_recvd = recvfrom(receiver->socket, (void *) &receiver->message, *len, 0, (struct sockaddr *) NULL, 0); /* verify rtp header */ if (receiver->message.header.version != 2) { *len = 0; return -1; } #if PRINT_DEBUG fprintf(stderr, "%d octets received from SSRC %u\n", octets_recvd, receiver->message.header.ssrc); #endif #if VERBOSE_DEBUG srtp_print_packet(&receiver->message.header, octets_recvd); #endif /* apply srtp */ stat = srtp_unprotect(receiver->srtp_ctx, &receiver->message.header, &octets_recvd); if (stat) { fprintf(stderr, "error: srtp unprotection failed with code %d%s\n", stat, stat == err_status_replay_fail ? " (replay check failed)" : stat == err_status_auth_fail ? " (auth check failed)" : ""); return -1; } strncpy(msg, receiver->message.body, octets_recvd); return octets_recvd; }
static int srtp_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->rtp.socket,m->b_wptr,m->b_datap->db_lim-m->b_datap->db_base,flags,from,fromlen); if (err>0){ /* keep NON-RTP data unencrypted */ rtp_header_t *rtp; if (err>=RTP_FIXED_HEADER_SIZE) { rtp = (rtp_header_t*)m->b_wptr; if (rtp->version!=2) { return err; } } slen=err; if (srtp_unprotect(srtp,m->b_wptr,&slen)==err_status_ok) return slen; else { ortp_error("srtp_unprotect() failed"); return -1; } } return err; }
/* 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); }
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 */ }
int evrb_decrypt(void* distant_ctx, void* data, size_t* len) { #ifdef USE_SRTP if (distant_ctx == NULL || ((EVRB_CRYPTO*)distant_ctx)->srtp_session == NULL) return -1; if(srtp_unprotect(((EVRB_CRYPTO*)distant_ctx)->srtp_session, data, (int*)len)) return -1; #endif return 0; }
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; }
JNIEXPORT jint JNICALL Java_org_theonionphone_protocol_SrtpUnwrapper_unwrapPacketNative (JNIEnv *env, jobject obj, jbyteArray packet) { jbyte* packetNative = (*env)->GetPrimitiveArrayCritical(env, packet, NULL); if(packetNative == NULL) { return 1; } int packetLength = srtpPacketSize; err_status_t status = srtp_unprotect((*srtpContextRecv), packetNative, &packetLength); (*env)->ReleasePrimitiveArrayCritical(env, packet, packetNative, 0); return status; }
int SrtpChannel::unprotectRtp(char* buffer, int *len) { if (!active_) return 0; int val = srtp_unprotect(receive_session_, (char*) buffer, len); if (val == 0) { return 0; } else { rtcpheader* head = reinterpret_cast<rtcpheader*>(buffer); rtpheader* headrtp = reinterpret_cast<rtpheader*>(buffer); ELOG_WARN("Error SrtpChannel::unprotectRtp %u packettype %d pt %d", val,head->packettype, headrtp->payloadtype); return -1; } }
void rtp_decoder_handle_pkt(u_char *arg, const struct pcap_pkthdr *hdr, const u_char *bytes){ rtp_decoder_t dcdr = (rtp_decoder_t)arg; int pktsize; struct timeval delta; int octets_recvd; srtp_err_status_t status; dcdr->frame_nr++; if (dcdr->start_tv.tv_sec == 0 && dcdr->start_tv.tv_sec == 0) { dcdr->start_tv = hdr->ts; } if (hdr->caplen < dcdr->rtp_offset) { return; } const void *rtp_packet = bytes + dcdr->rtp_offset; memcpy((void *)&dcdr->message, rtp_packet, hdr->caplen - dcdr->rtp_offset); pktsize = hdr->caplen - dcdr->rtp_offset; octets_recvd = pktsize; if (octets_recvd == -1) { return; } /* verify rtp header */ if (dcdr->message.header.version != 2) { return; //return -1; } if(dcdr->srtp_ctx == NULL){ status = rtp_decoder_init_srtp(dcdr, dcdr->message.header.ssrc); if (status) { exit(1); } } if(dcdr->srtp_ctx != NULL){ } status = srtp_unprotect(dcdr->srtp_ctx, &dcdr->message, &octets_recvd); if (status){ return; } timersub(&hdr->ts, &dcdr->start_tv, &delta); fprintf(stdout, "%02ld:%02ld.%06ld\n", delta.tv_sec/60, delta.tv_sec%60, (long)delta.tv_usec); hexdump(&dcdr->message, pktsize); }
int rtp_un(srtp_t srtp, struct mbuf *mb) { int err, len; if(srtp) { mbuf_advance(mb, -RTP_HEADER_SIZE); len = (int)mbuf_get_left(mb); err = srtp_unprotect(srtp, mbuf_buf(mb), &len); if(err) { printf("srtp unprotect fail %d\n", err); return -1; } mbuf_advance(mb, RTP_HEADER_SIZE); len -= RTP_HEADER_SIZE; } else { len = (int)mbuf_get_left(mb); } return len; }
static int our_srtp_decrypt (void *foo, unsigned char *buffer, unsigned int *len) { err_status_t err; int retdata; srtp_ctx_t *srtp_ctx = (srtp_ctx_t *)foo; retdata = *len; err = srtp_unprotect(srtp_ctx, (void *)buffer, &retdata); if (err != 0) { return FALSE; } *len = retdata; return TRUE; }
nsresult SrtpFlow::UnprotectRtp(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(session_, in, &len); if (r != err_status_ok) { MOZ_MTLOG(PR_LOG_ERROR, "Error unprotecting SRTP packet"); return NS_ERROR_FAILURE; } MOZ_ASSERT(len <= max_len); *out_len = len; MOZ_MTLOG(PR_LOG_DEBUG, "Successfully unprotected an SRTP packet of len " << *out_len); return NS_OK; }
nsresult SrtpFlow::UnprotectRtp(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(session_, in, &len); if (r != srtp_err_status_ok) { CSFLogError(LOGTAG, "Error unprotecting SRTP packet error=%d", (int)r); return NS_ERROR_FAILURE; } MOZ_ASSERT(len <= max_len); *out_len = len; CSFLogDebug(LOGTAG, "Successfully unprotected an SRTP packet of len %d", *out_len); return NS_OK; }
double srtp_rejections_per_second(int msg_len_octets, const srtp_policy_t *policy) { srtp_ctx_t *srtp; srtp_hdr_t *mesg; int i; int len; clock_t timer; int num_trials = 1000000; uint32_t ssrc = policy->ssrc.value; err_status_t status; /* * allocate and initialize an srtp session */ status = srtp_create(&srtp, policy); if (status) { printf("error: srtp_create() failed with error code %d\n", status); exit(1); } mesg = srtp_create_test_packet(msg_len_octets, ssrc); if (mesg == NULL) return 0.0; /* indicate failure by returning zero */ len = msg_len_octets; srtp_protect(srtp, (srtp_hdr_t *)mesg, &len); timer = clock(); for (i=0; i < num_trials; i++) { len = msg_len_octets; srtp_unprotect(srtp, (srtp_hdr_t *)mesg, &len); } timer = clock() - timer; free(mesg); return (double) num_trials * CLOCKS_PER_SEC / timer; }
int krx_udp_receive(udp_conn* c) { socklen_t len = sizeof(c->client); ssize_t nread = recvfrom(c->sock, c->buf, KRX_UDP_BUF_LEN, 0, (struct sockaddr*)&c->client, &len); if(nread < 0) { printf("Error: cannot receive.\n"); return -1; } if(nread < 2) { printf("Only received 2 bytes?\n"); return 0; } if((c->buf[0] == 0x00 || c->buf[0] == 0x01) && (c->buf[1] == 0x00 || c->buf[1] == 0x01) ) { handle_stun(c, c->buf, nread); } else { if(krx_dtls_is_handshake_done(&c->dtls) > 0) { if(c->state == KRX_STATE_NONE) { // when done, we pass on the data libsrtp c->state = KRX_STATE_SSL_INIT_READY; printf("---------------------- finished --------------------------\n"); uint8_t material[KRX_SRTP_MASTER_LEN * 2]; int r = SSL_export_keying_material(c->dtls.ssl, material, KRX_SRTP_MASTER_LEN * 2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0); if(r == 0) { printf("Error: cannot export the SSL keying material.\n"); exit(EXIT_FAILURE); } // extracking keying example https://github.com/traviscross/baresip/blob/8974d662c942b10a9bb05223ddc7881896dd4c2f/modules/dtls_srtp/tls_udp.c /* Keys:: http://tools.ietf.org/html/rfc5764#section-4.2, note: client <> server use different keying, we handle server for now. */ uint8_t* remote_key = material; uint8_t* local_key = remote_key + KRX_SRTP_MASTER_KEY_LEN; uint8_t* remote_salt = local_key + KRX_SRTP_MASTER_KEY_LEN; uint8_t* local_salt = remote_salt + KRX_SRTP_MASTER_SALT_LEN;; memcpy(c->srtp.policy.key, remote_key, KRX_SRTP_MASTER_KEY_LEN); memcpy(c->srtp.policy.key + KRX_SRTP_MASTER_KEY_LEN, remote_salt, KRX_SRTP_MASTER_SALT_LEN); SRTP_PROTECTION_PROFILE *p = SSL_get_selected_srtp_profile(c->dtls.ssl); if(!p) { printf("Error: cannot extract the srtp_profile.\n"); exit(EXIT_FAILURE); } printf(">>>>>>> %s <<<<<\n", p->name); // TLS_RSA_WITH_AES_128_CBC_SHA printf("---> cipher: %s\n", SSL_CIPHER_get_name(SSL_get_current_cipher(c->dtls.ssl))); /* create SRTP session */ err_status_t sr = srtp_create(&c->srtp.session, &c->srtp.policy); if(sr != err_status_ok) { printf("Error: cannot create srtp session: %d.\n", sr); exit(EXIT_FAILURE); } /* @TODO --- CLEANUP! - WE NEED TO UNPROTECT THIS DIRECTLY!!! SEE "MARKER-MARKER" below*/ int buflen = nread; sr = srtp_unprotect(c->srtp.session, c->buf, &buflen); if(sr != err_status_ok) { printf("Error: cannot unprotect, err: %d. len: %d <> %d\n", sr, len, buflen); } else { //printf("~ %zd bytes read // buflen: %d.\n", nread, buflen); krx_rtp_decode(&c->rtp, c->buf, buflen); } } else if(c->state == KRX_STATE_SSL_INIT_READY) { /* @TODO --- CLEANUP! duplicate, see a couple of line above */ /* MARKER-MARKER */ int buflen = nread; err_status_t sr = srtp_unprotect(c->srtp.session, c->buf, &buflen); if(sr != err_status_ok) { printf("Error: cannot unprotect, err: %d. len: %d <> %d\n", sr, len, buflen); } else { //printf("~ %zd bytes read // buflen: %d.\n", nread, buflen); krx_rtp_decode(&c->rtp, c->buf, buflen); } } } else { krx_dtls_handle_traffic(&c->dtls, c->buf, nread); } } return 0; }
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; }
static int ozrtp_rtp_recvfrom(RtpTransport *t, mblk_t *m, int flags, struct sockaddr *from, socklen_t *fromlen){ int rlen; ZrtpContext *zrtpContext = (ZrtpContext*) t->data; OrtpZrtpContext *userData = (OrtpZrtpContext*) zrtpContext->userData; // Do extra stuff first check_timer(zrtpContext, userData); // Check if something to receive rlen=rtp_session_rtp_recv_abstract(t->session->rtp.socket,m,flags,from,fromlen); if (rlen<=0) { // nothing was received or error: pass the information to caller return rlen; } uint8_t* rtp = m->b_rptr; int rtpVersion = ((rtp_header_t*)rtp)->version; // If plain or secured RTP if (rtpVersion == 2) { if (userData->srtpRecv != NULL && zrtp_inState(zrtpContext, SecureState)) { // probably srtp packet, unprotect err_status_t err = srtp_unprotect(userData->srtpRecv,m->b_wptr,&rlen); if (err != err_status_ok) { ortp_warning("srtp_unprotect failed; packet may be plain RTP"); } } // in both cases (RTP plain and deciphered srtp) return rlen; } // if ZRTP packet, send to engine uint32_t *magicField=(uint32_t *)(rtp + 4); if (rlen >= ZRTP_MIN_MSG_LENGTH && rtpVersion==0 && ntohl(*magicField) == ZRTP_MAGIC) { print_zrtp_packet("received", rtp); uint8_t *ext_header = rtp+ZRTP_MESSAGE_OFFSET; uint16_t ext_length = get_zrtp_message_length(ext_header); char messageType[9]; parseZrtpMessageType(messageType, ext_header); // Check max length if (rlen < 12 + ext_length + 4) { ortp_warning("Received malformed ZRTP-like packet: size %d (expected %d)", rlen, 12 + ext_length + 4); return 0; } // Check sequence number uint16_t seq_number = get_rtp_seqnumber(rtp); if (userData->last_recv_zrtp_seq_number != 0 && seq_number <= userData->last_recv_zrtp_seq_number) { // Discard out of order ZRTP packet ortp_message("Discarding received out of order zrtp packet: %d (expected >%d)", seq_number, userData->last_recv_zrtp_seq_number); return 0; } // Check packet checksum uint32_t rcv_crc = get_zrtp_packet_crc((uint32_t*)rtp, ext_length); uint32_t zrtp_total_packet_length = ZRTP_MESSAGE_OFFSET + 4*ext_length + 4; if (!zrtp_CheckCksum(rtp, zrtp_total_packet_length-CRC_SIZE, rcv_crc)) { ortp_warning("Bad ZRTP packet checksum %u total %u", rcv_crc, zrtp_total_packet_length); return 0; } uint32_t peerssrc = ntohl(*(uint32_t*)(rtp+8)); #if HAVE_zrtpcpp_with_len zrtp_processZrtpMessage(zrtpContext, ext_header, peerssrc,rlen); #else zrtp_processZrtpMessage(zrtpContext, ext_header, peerssrc); #endif userData->last_recv_zrtp_seq_number=seq_number; return 0; } else { // Not a ZRTP packet, accept it return rlen; } }
/* * 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 srtp_test(const srtp_policy_t *policy) { int i; srtp_t srtp_sender; srtp_t srtp_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(&srtp_sender, policy)); /* print out policy */ err_check(srtp_session_print_policy(srtp_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(srtp_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() 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(&srtp_rcvr, rcvr_policy)); err_check(srtp_unprotect(srtp_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(srtp_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(srtp_sender, hdr, &len)); /* flip bits in packet */ data[0] ^= 0xff; /* unprotect, and check for authentication failure */ status = srtp_unprotect(srtp_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(srtp_sender)); err_check(srtp_dealloc(srtp_rcvr)); free(hdr); free(hdr2); return err_status_ok; }
/* * This callback is called by transport when incoming rtp is received */ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size) { transport_srtp *srtp = (transport_srtp *) user_data; int len = (int)size; srtp_err_status_t err; void (*cb)(void*, void*, pj_ssize_t) = NULL; void *cb_data = NULL; if (srtp->bypass_srtp) { srtp->rtp_cb(srtp->user_data, pkt, size); return; } if (size < 0) { return; } /* Give the packet to keying first by invoking its send_rtp() op. * Yes, the usage of send_rtp() is rather hacky, but it is convenient * as the signature suits the purpose and it is ready to use * (no futher registration/setting needed), and it may never be used * by any keying method in the future. */ { unsigned i; pj_status_t status; for (i=0; i < srtp->keying_cnt; i++) { if (!srtp->keying[i]->op->send_rtp) continue; status = pjmedia_transport_send_rtp(srtp->keying[i], pkt, size); if (status != PJ_EIGNORED) { /* Packet is already consumed by the keying method */ return; } } } /* Make sure buffer is 32bit aligned */ PJ_ASSERT_ON_FAIL( (((pj_ssize_t)pkt) & 0x03)==0, return ); if (srtp->probation_cnt > 0) --srtp->probation_cnt; pj_lock_acquire(srtp->mutex); if (!srtp->session_inited) { pj_lock_release(srtp->mutex); return; } err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); if (srtp->probation_cnt > 0 && (err == srtp_err_status_replay_old || err == srtp_err_status_replay_fail)) { /* Handle such condition that stream is updated (RTP seq is reinited * & SRTP is restarted), but some old packets are still coming * so SRTP is learning wrong RTP seq. While the newly inited RTP seq * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect() * will return err_status_replay_*. Restarting SRTP can resolve this. */ pjmedia_srtp_crypto tx, rx; pj_status_t status; tx = srtp->tx_policy; rx = srtp->rx_policy; status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp, &tx, &rx); if (status != PJ_SUCCESS) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s", get_libsrtp_errstr(err))); } else if (!srtp->bypass_srtp) { err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len); } } if (err != srtp_err_status_ok) { PJ_LOG(5,(srtp->pool->obj_name, "Failed to unprotect SRTP, pkt size=%d, err=%s", size, get_libsrtp_errstr(err))); } else { cb = srtp->rtp_cb; cb_data = srtp->user_data; } pj_lock_release(srtp->mutex); if (cb) { (*cb)(cb_data, pkt, len); } }
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; }