RefPtr<SrtpFlow> SrtpFlow::Create(int cipher_suite, bool inbound, const void *key, size_t key_len) { nsresult res = Init(); if (!NS_SUCCEEDED(res)) return nullptr; RefPtr<SrtpFlow> flow = new SrtpFlow(); if (!key) { CSFLogError(LOGTAG, "Null SRTP key specified"); return nullptr; } if (key_len != SRTP_TOTAL_KEY_LENGTH) { CSFLogError(LOGTAG, "Invalid SRTP key length"); return nullptr; } srtp_policy_t policy; memset(&policy, 0, sizeof(srtp_policy_t)); // Note that we set the same cipher suite for RTP and RTCP // since any flow can only have one cipher suite with DTLS-SRTP switch (cipher_suite) { case SRTP_AES128_CM_HMAC_SHA1_80: CSFLogDebug(LOGTAG, "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_80"); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtp); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); break; case SRTP_AES128_CM_HMAC_SHA1_32: CSFLogDebug(LOGTAG, "Setting SRTP cipher suite SRTP_AES128_CM_HMAC_SHA1_32"); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&policy.rtp); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&policy.rtcp); // 80-bit per RFC 5764 break; // S 4.1.2. default: CSFLogError(LOGTAG, "Request to set unknown SRTP cipher suite"); return nullptr; } // This key is copied into the srtp_t object, so we don't // need to keep it. policy.key = const_cast<unsigned char *>( static_cast<const unsigned char *>(key)); policy.ssrc.type = inbound ? ssrc_any_inbound : ssrc_any_outbound; policy.ssrc.value = 0; policy.ekt = nullptr; policy.window_size = 1024; // Use the Chrome value. Needs to be revisited. Default is 128 policy.allow_repeat_tx = 1; // Use Chrome value; needed for NACK mode to work policy.next = nullptr; // Now make the session srtp_err_status_t r = srtp_create(&flow->session_, &policy); if (r != srtp_err_status_ok) { CSFLogError(LOGTAG, "Error creating srtp session"); return nullptr; } return flow; }
void janus_dtls_srtp_incoming_msg(janus_dtls_srtp *dtls, char *buf, uint16_t len) { if(dtls == NULL) { JANUS_LOG(LOG_ERR, "No DTLS-SRTP stack, no incoming message...\n"); return; } janus_ice_component *component = (janus_ice_component *)dtls->component; if(component == NULL) { JANUS_LOG(LOG_ERR, "No component, no DTLS...\n"); return; } janus_ice_stream *stream = component->stream; if(!stream) { JANUS_LOG(LOG_ERR, "No stream, no DTLS...\n"); return; } janus_ice_handle *handle = stream->handle; if(!handle || !handle->agent) { JANUS_LOG(LOG_ERR, "No handle/agent, no DTLS...\n"); return; } if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT)) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Alert already triggered, clearing up...\n", handle->handle_id); return; } if(!dtls->ssl || !dtls->read_bio) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No DTLS stuff for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); return; } if(dtls->dtls_started == 0) { /* Handshake not started yet: maybe we're still waiting for the answer and the DTLS role? */ return; } janus_dtls_fd_bridge(dtls); int written = BIO_write(dtls->read_bio, buf, len); if(written != len) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Only written %d/%d of those bytes on the read BIO...\n", handle->handle_id, written, len); } else { JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Written %d bytes on the read BIO...\n", handle->handle_id, written); } janus_dtls_fd_bridge(dtls); /* Try to read data */ char data[1500]; /* FIXME */ memset(&data, 0, 1500); int read = SSL_read(dtls->ssl, &data, 1500); JANUS_LOG(LOG_HUGE, "[%"SCNu64"] ... and read %d of them from SSL...\n", handle->handle_id, read); if(read < 0) { unsigned long err = SSL_get_error(dtls->ssl, read); if(err == SSL_ERROR_SSL) { /* Ops, something went wrong with the DTLS handshake */ char error[200]; ERR_error_string_n(ERR_get_error(), error, 200); JANUS_LOG(LOG_ERR, "[%"SCNu64"] Handshake error: %s\n", handle->handle_id, error); return; } } janus_dtls_fd_bridge(dtls); if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_STOP) || janus_is_stopping()) { /* DTLS alert triggered, we should end it here */ JANUS_LOG(LOG_VERB, "[%"SCNu64"] Forced to stop it here...\n", handle->handle_id); return; } if(!SSL_is_init_finished(dtls->ssl)) { /* Nothing else to do for now */ JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Initialization not finished yet...\n", handle->handle_id); return; } if(dtls->ready) { /* There's data to be read? */ JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Any data available?\n", handle->handle_id); #ifdef HAVE_SCTP if(dtls->sctp != NULL && read > 0) { JANUS_LOG(LOG_HUGE, "[%"SCNu64"] Sending data (%d bytes) to the SCTP stack...\n", handle->handle_id, read); janus_sctp_data_from_dtls(dtls->sctp, data, read); } #else if(read > 0) { JANUS_LOG(LOG_WARN, "[%"SCNu64"] Data available but Data Channels support disabled...\n", handle->handle_id); } #endif } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] DTLS established, yay!\n", handle->handle_id); /* Check the remote fingerprint */ X509 *rcert = SSL_get_peer_certificate(dtls->ssl); if(!rcert) { JANUS_LOG(LOG_ERR, "[%"SCNu64"] No remote certificate?? (%s)\n", handle->handle_id, ERR_reason_error_string(ERR_get_error())); } else { unsigned int rsize; unsigned char rfingerprint[EVP_MAX_MD_SIZE]; char remote_fingerprint[160]; char *rfp = (char *)&remote_fingerprint; if(stream->remote_hashing && !strcasecmp(stream->remote_hashing, "sha-1")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-1 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha1(), (unsigned char *)rfingerprint, &rsize); } else { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Computing sha-256 fingerprint of remote certificate...\n", handle->handle_id); X509_digest(rcert, EVP_sha256(), (unsigned char *)rfingerprint, &rsize); } X509_free(rcert); rcert = NULL; unsigned int i = 0; for(i = 0; i < rsize; i++) { g_snprintf(rfp, 4, "%.2X:", rfingerprint[i]); rfp += 3; } *(rfp-1) = 0; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Remote fingerprint (%s) of the client is %s\n", handle->handle_id, stream->remote_hashing ? stream->remote_hashing : "sha-256", remote_fingerprint); if(!strcasecmp(remote_fingerprint, stream->remote_fingerprint ? stream->remote_fingerprint : "(none)")) { JANUS_LOG(LOG_VERB, "[%"SCNu64"] Fingerprint is a match!\n", handle->handle_id); dtls->dtls_state = JANUS_DTLS_STATE_CONNECTED; dtls->dtls_connected = janus_get_monotonic_time(); /* Notify event handlers */ janus_dtls_notify_state_change(dtls); } else { /* FIXME NOT a match! MITM? */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Fingerprint is NOT a match! got %s, expected %s\n", handle->handle_id, remote_fingerprint, stream->remote_fingerprint); dtls->dtls_state = JANUS_DTLS_STATE_FAILED; /* Notify event handlers */ janus_dtls_notify_state_change(dtls); goto done; } if(dtls->dtls_state == JANUS_DTLS_STATE_CONNECTED) { /* Which SRTP profile is being negotiated? */ SRTP_PROTECTION_PROFILE *srtp_profile = SSL_get_selected_srtp_profile(dtls->ssl); if(srtp_profile == NULL) { /* Should never happen, but just in case... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] No SRTP profile selected...\n", handle->handle_id); dtls->dtls_state = JANUS_DTLS_STATE_FAILED; /* Notify event handlers */ janus_dtls_notify_state_change(dtls); goto done; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] %s\n", handle->handle_id, srtp_profile->name); int key_length = 0, salt_length = 0, master_length = 0; switch(srtp_profile->id) { case SRTP_AES128_CM_SHA1_80: case SRTP_AES128_CM_SHA1_32: key_length = SRTP_MASTER_KEY_LENGTH; salt_length = SRTP_MASTER_SALT_LENGTH; master_length = SRTP_MASTER_LENGTH; break; #ifdef HAVE_SRTP_AESGCM case SRTP_AEAD_AES_256_GCM: key_length = SRTP_AESGCM256_MASTER_KEY_LENGTH; salt_length = SRTP_AESGCM256_MASTER_SALT_LENGTH; master_length = SRTP_AESGCM256_MASTER_LENGTH; break; case SRTP_AEAD_AES_128_GCM: key_length = SRTP_AESGCM128_MASTER_KEY_LENGTH; salt_length = SRTP_AESGCM128_MASTER_SALT_LENGTH; master_length = SRTP_AESGCM128_MASTER_LENGTH; break; #endif default: /* Will never happen? */ JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %lu\n", handle->handle_id, srtp_profile->id); break; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] Key/Salt/Master: %d/%d/%d\n", handle->handle_id, master_length, key_length, salt_length); /* Complete with SRTP setup */ unsigned char material[master_length*2]; unsigned char *local_key, *local_salt, *remote_key, *remote_salt; /* Export keying material for SRTP */ if(!SSL_export_keying_material(dtls->ssl, material, master_length*2, "EXTRACTOR-dtls_srtp", 19, NULL, 0, 0)) { /* Oops... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, couldn't extract SRTP keying material for component %d in stream %d?? (%s)\n", handle->handle_id, component->component_id, stream->stream_id, ERR_reason_error_string(ERR_get_error())); goto done; } /* Key derivation (http://tools.ietf.org/html/rfc5764#section-4.2) */ if(dtls->dtls_role == JANUS_DTLS_ROLE_CLIENT) { local_key = material; remote_key = local_key + key_length; local_salt = remote_key + key_length; remote_salt = local_salt + salt_length; } else { remote_key = material; local_key = remote_key + key_length; remote_salt = local_key + key_length; local_salt = remote_salt + salt_length; } /* Build master keys and set SRTP policies */ /* Remote (inbound) */ switch(srtp_profile->id) { case SRTP_AES128_CM_SHA1_80: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtcp)); break; case SRTP_AES128_CM_SHA1_32: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->remote_policy.rtcp)); break; #ifdef HAVE_SRTP_AESGCM case SRTP_AEAD_AES_256_GCM: srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->remote_policy.rtcp)); break; case SRTP_AEAD_AES_128_GCM: srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->remote_policy.rtp)); srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->remote_policy.rtcp)); break; #endif default: /* Will never happen? */ JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %s\n", handle->handle_id, srtp_profile->name); break; } dtls->remote_policy.ssrc.type = ssrc_any_inbound; unsigned char remote_policy_key[master_length]; dtls->remote_policy.key = (unsigned char *)&remote_policy_key; memcpy(dtls->remote_policy.key, remote_key, key_length); memcpy(dtls->remote_policy.key + key_length, remote_salt, salt_length); #if HAS_DTLS_WINDOW_SIZE dtls->remote_policy.window_size = 128; dtls->remote_policy.allow_repeat_tx = 0; #endif dtls->remote_policy.next = NULL; /* Local (outbound) */ switch(srtp_profile->id) { case SRTP_AES128_CM_SHA1_80: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtcp)); break; case SRTP_AES128_CM_SHA1_32: srtp_crypto_policy_set_aes_cm_128_hmac_sha1_32(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_cm_128_hmac_sha1_80(&(dtls->local_policy.rtcp)); break; #ifdef HAVE_SRTP_AESGCM case SRTP_AEAD_AES_256_GCM: srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_gcm_256_16_auth(&(dtls->local_policy.rtcp)); break; case SRTP_AEAD_AES_128_GCM: srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->local_policy.rtp)); srtp_crypto_policy_set_aes_gcm_128_16_auth(&(dtls->local_policy.rtcp)); break; #endif default: /* Will never happen? */ JANUS_LOG(LOG_WARN, "[%"SCNu64"] Unsupported SRTP profile %s\n", handle->handle_id, srtp_profile->name); break; } dtls->local_policy.ssrc.type = ssrc_any_outbound; unsigned char local_policy_key[master_length]; dtls->local_policy.key = (unsigned char *)&local_policy_key; memcpy(dtls->local_policy.key, local_key, key_length); memcpy(dtls->local_policy.key + key_length, local_salt, salt_length); #if HAS_DTLS_WINDOW_SIZE dtls->local_policy.window_size = 128; dtls->local_policy.allow_repeat_tx = 0; #endif dtls->local_policy.next = NULL; /* Create SRTP sessions */ srtp_err_status_t res = srtp_create(&(dtls->srtp_in), &(dtls->remote_policy)); if(res != srtp_err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating inbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_srtp_error_str(res)); goto done; } JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created inbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); res = srtp_create(&(dtls->srtp_out), &(dtls->local_policy)); if(res != srtp_err_status_ok) { /* Something went wrong... */ JANUS_LOG(LOG_ERR, "[%"SCNu64"] Oops, error creating outbound SRTP session for component %d in stream %d??\n", handle->handle_id, component->component_id, stream->stream_id); JANUS_LOG(LOG_ERR, "[%"SCNu64"] -- %d (%s)\n", handle->handle_id, res, janus_srtp_error_str(res)); goto done; } dtls->srtp_profile = srtp_profile->id; dtls->srtp_valid = 1; JANUS_LOG(LOG_VERB, "[%"SCNu64"] Created outbound SRTP session for component %d in stream %d\n", handle->handle_id, component->component_id, stream->stream_id); #ifdef HAVE_SCTP if(janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_DATA_CHANNELS)) { /* Create SCTP association as well */ janus_dtls_srtp_create_sctp(dtls); } #endif dtls->ready = 1; } done: if(!janus_flags_is_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_ALERT) && dtls->srtp_valid) { /* Handshake successfully completed */ janus_ice_dtls_handshake_done(handle, component); } else { /* Something went wrong in either DTLS or SRTP... tell the plugin about it */ janus_dtls_callback(dtls->ssl, SSL_CB_ALERT, 0); janus_flags_set(&handle->webrtc_flags, JANUS_ICE_HANDLE_WEBRTC_CLEANING); } } } }