Files
webrtc/internal/dtls/dtls.c
Sean DuBois c23159338c Implement SRTP
RTP packets are successfully decrypted
2018-06-09 16:10:19 -07:00

362 lines
8.5 KiB
C

#include "dtls.h"
#define ONE_YEAR 60*60*24*365
int dtls_trivial_verify_callback(int preverify_ok, X509_STORE_CTX *ctx) {
(void) preverify_ok;
(void) ctx;
return 1;
}
SSL_CTX* dtls_ctx_init (tlscfg *cfg) {
SSL_CTX* ctx = SSL_CTX_new(DTLS_method());
SSL_CTX_set_read_ahead(ctx, 1);
SSL_CTX_set_ecdh_auto(ctx, true);
SSL_CTX_set_verify(ctx, SSL_VERIFY_PEER | SSL_VERIFY_FAIL_IF_NO_PEER_CERT, dtls_trivial_verify_callback);
if (SSL_CTX_set_tlsext_use_srtp(ctx, "SRTP_AES128_CM_SHA1_32:SRTP_AES128_CM_SHA1_80") != 0) {
goto error;
}
if (!SSL_CTX_use_certificate(ctx, cfg->cert)) {
goto error;
}
if (!SSL_CTX_use_PrivateKey(ctx, cfg->pkey) ||
!SSL_CTX_check_private_key(ctx)) {
goto error;
}
if (!SSL_CTX_set_cipher_list(ctx, "HIGH:!aNULL:!MD5:!RC4")) {
goto error;
}
return ctx;
error:
if (ctx != NULL) {
SSL_CTX_free(ctx);
}
return NULL;
}
dtls_sess* dtls_sess_new(SSL_CTX* sslcfg, int con_state) {
dtls_sess* sess = (dtls_sess*)calloc(1, sizeof(dtls_sess));
BIO* rbio = NULL;
BIO* wbio = NULL;
sess->state = con_state;
if (NULL == (sess->ssl = SSL_new(sslcfg))) {
goto error;
}
if (NULL == (rbio = BIO_new(BIO_s_mem()))) {
goto error;
}
BIO_set_mem_eof_return(rbio, -1);
if (NULL == (wbio = BIO_new(BIO_s_mem()))) {
BIO_free(rbio);
rbio = NULL;
goto error;
}
BIO_set_mem_eof_return(wbio, -1);
SSL_set_bio(sess->ssl, rbio, wbio);
if (sess->state == DTLS_CONSTATE_PASS) {
SSL_set_accept_state(sess->ssl);
} else {
SSL_set_connect_state(sess->ssl);
}
sess->type = DTLS_CONTYPE_NEW;
pthread_mutex_init(&sess->lock, NULL);
return sess;
error:
if (sess->ssl != NULL) {
SSL_free(sess->ssl);
sess->ssl = NULL;
}
free(sess);
return NULL;
}
void dtls_sess_free(dtls_sess* sess) {
if (sess->ssl != NULL) {
SSL_free(sess->ssl);
sess->ssl = NULL;
}
pthread_mutex_destroy(&sess->lock);
free(sess);
}
extern void go_handle_sendto(const char* src, const char* dst, char* buf,
int len);
ptrdiff_t dtls_sess_send_pending(dtls_sess* sess, const char* src,
const char* dst) {
if (sess->ssl == NULL) {
return -2;
}
BIO* wbio = SSL_get_wbio(sess->ssl);
size_t pending = BIO_ctrl_pending(wbio);
size_t len = 0;
if (pending > 0) {
char* buf = malloc(pending);
len = BIO_read(wbio, buf, pending);
buf = realloc(buf, len);
go_handle_sendto(src, dst, buf, len);
return len;
}
return 0;
}
ptrdiff_t dtls_sess_put_packet(dtls_sess* sess, const char* src,
const char* dst, const void* buf, size_t len) {
if (sess->ssl == NULL) {
return -1;
}
ptrdiff_t ret = 0;
char dummy[len];
pthread_mutex_lock(&sess->lock);
pthread_mutex_unlock(&sess->lock);
BIO* rbio = SSL_get_rbio(sess->ssl);
if (sess->state == DTLS_CONSTATE_ACTPASS) {
sess->state = DTLS_CONSTATE_PASS;
SSL_set_accept_state(sess->ssl);
}
dtls_sess_send_pending(sess, src, dst);
BIO_write(rbio, buf, len);
ret = SSL_read(sess->ssl, dummy, len);
if ((ret < 0) && SSL_get_error(sess->ssl, ret) == SSL_ERROR_SSL) {
return ret;
}
ret = dtls_sess_send_pending(sess, src, dst);
if (SSL_is_init_finished(sess->ssl)) {
sess->type = DTLS_CONTYPE_EXISTING;
}
return ret;
}
static inline enum dtls_con_state dtls_sess_get_state(const dtls_sess* sess) {
return sess->state;
}
ptrdiff_t dtls_do_handshake(dtls_sess* sess, const char* src, const char* dst) {
if (sess->ssl == NULL ||
(dtls_sess_get_state(sess) != DTLS_CONSTATE_ACT &&
dtls_sess_get_state(sess) != DTLS_CONSTATE_ACTPASS)) {
return -2;
}
if (dtls_sess_get_state(sess) == DTLS_CONSTATE_ACTPASS) {
sess->state = DTLS_CONSTATE_ACT;
}
SSL_do_handshake(sess->ssl);
pthread_mutex_lock(&sess->lock);
ptrdiff_t ret = dtls_sess_send_pending(sess, src, dst);
pthread_mutex_unlock(&sess->lock);
return ret;
}
tlscfg* dtls_build_tlscfg() {
tlscfg* cfg = (tlscfg*)calloc(1, sizeof(tlscfg));
static const int num_bits = 2048;
BIGNUM *bne = BN_new();
if(bne == NULL) {
goto error;
}
if(!BN_set_word(bne, RSA_F4)) {
goto error;
}
RSA *rsa_key = RSA_new();
if(rsa_key == NULL) {
goto error;
}
if(!RSA_generate_key_ex(rsa_key, num_bits, bne, NULL)) {
goto error;
}
if((cfg->pkey = EVP_PKEY_new()) == NULL) {
goto error;
}
if(!EVP_PKEY_assign_RSA(cfg->pkey, rsa_key)) {
goto error;
}
rsa_key = NULL;
if((cfg->cert = X509_new()) == NULL) {
goto error;
}
X509_set_version(cfg->cert, 2);
ASN1_INTEGER_set(X509_get_serialNumber(cfg->cert), 1000); // TODO
X509_gmtime_adj(X509_get_notBefore(cfg->cert), -1 * ONE_YEAR);
X509_gmtime_adj(X509_get_notAfter(cfg->cert), ONE_YEAR);
if(!X509_set_pubkey(cfg->cert, cfg->pkey)) {
goto error;
}
X509_NAME *cert_name = cert_name = X509_get_subject_name(cfg->cert);
if(cert_name == NULL) {
goto error;
}
const char *name = "pion-webrtc";
X509_NAME_add_entry_by_txt(cert_name, "O", MBSTRING_ASC, (const char unsigned *)name, -1, -1, 0);
X509_NAME_add_entry_by_txt(cert_name, "CN", MBSTRING_ASC, (const char unsigned *)name, -1, -1, 0);
if(!X509_set_issuer_name(cfg->cert, cert_name)) {
goto error;
}
if(!X509_sign(cfg->cert, cfg->pkey, EVP_sha1())) {
goto error;
}
BN_free(bne);
return cfg;
error:
if(bne)
BN_free(bne);
if(rsa_key && !cfg && !cfg->pkey)
RSA_free(rsa_key);
if(cfg && cfg->pkey)
EVP_PKEY_free(cfg->pkey);
if(cfg && cfg->cert)
X509_free(cfg->cert);
return NULL;
}
SSL_CTX* dtls_build_sslctx(tlscfg* cfg) {
if (cfg == NULL) {
return NULL;
}
return dtls_ctx_init(cfg);
}
dtls_sess* dtls_build_session(SSL_CTX* cfg, bool is_server) {
return dtls_sess_new(cfg, is_server);
}
bool openssl_global_init() {
OpenSSL_add_ssl_algorithms();
SSL_load_error_strings();
return SSL_library_init();
}
void dtls_session_cleanup(SSL_CTX* ssl_ctx, dtls_sess* dtls_session) {
if (dtls_session) {
dtls_sess_free(dtls_session);
}
if (ssl_ctx) {
SSL_CTX_free(ssl_ctx);
}
}
void dtls_tlscfg_cleanup(tlscfg* cfg) {
if (cfg) {
if (cfg->cert) {
X509_free(cfg->cert);
}
if (cfg->pkey) {
EVP_PKEY_free(cfg->pkey);
}
free(cfg);
}
}
dtls_handle_incoming_return *dtls_handle_incoming(dtls_sess* sess, const char* src, const char* dst,
void* buf, int len) {
ptrdiff_t stat = dtls_sess_put_packet(sess, src, dst, buf, len);
if (SSL_get_error(sess->ssl, stat) == SSL_ERROR_SSL) {
fprintf(stderr,
"DTLS failure occurred on dtls session %p due to reason '%s'\n",
sess, ERR_reason_error_string(ERR_get_error()));
return NULL;
}
if (sess->type == DTLS_CONTYPE_EXISTING) {
unsigned char dtls_buffer[SRTP_MASTER_KEY_KEY_LEN * 2 + SRTP_MASTER_KEY_SALT_LEN * 2];
const char *label = "EXTRACTOR-dtls_srtp";
if (!SSL_export_keying_material(sess->ssl, dtls_buffer, sizeof(dtls_buffer), label, strlen(label), NULL, 0, 0)) {
fprintf(stderr, "SSL_export_keying_material failed");
return NULL;
}
size_t offset = 0;
dtls_handle_incoming_return *ret = calloc(1, sizeof(dtls_handle_incoming_return));
ret->key_length = SRTP_MASTER_KEY_KEY_LEN + SRTP_MASTER_KEY_SALT_LEN;
memcpy(&ret->client_write_key[0], &dtls_buffer[offset], SRTP_MASTER_KEY_KEY_LEN);
offset += SRTP_MASTER_KEY_KEY_LEN;
memcpy(&ret->server_write_key[0], &dtls_buffer[offset], SRTP_MASTER_KEY_KEY_LEN);
offset += SRTP_MASTER_KEY_KEY_LEN;
memcpy(&ret->client_write_key[SRTP_MASTER_KEY_KEY_LEN], &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
offset += SRTP_MASTER_KEY_SALT_LEN;
memcpy(&ret->server_write_key[SRTP_MASTER_KEY_KEY_LEN], &dtls_buffer[offset], SRTP_MASTER_KEY_SALT_LEN);
switch(SSL_get_selected_srtp_profile(sess->ssl)->id) {
case SRTP_AES128_CM_SHA1_80:
memcpy(&ret->profile, "SRTP_AES128_CM_SHA1_80", strlen("SRTP_AES128_CM_SHA1_80"));
break;
case SRTP_AES128_CM_SHA1_32:
memcpy(&ret->profile, "SRTP_AES128_CM_SHA1_32", strlen("SRTP_AES128_CM_SHA1_32"));
break;
}
return ret;
}
return NULL;
}
char *dtls_tlscfg_fingerprint(tlscfg* cfg) {
if (cfg == NULL) {
return NULL;
}
unsigned int size;
unsigned char fingerprint[EVP_MAX_MD_SIZE];
if(X509_digest(cfg->cert, EVP_sha256(), (unsigned char *)fingerprint, &size) == 0) {
return NULL;
}
char *hex_fingeprint = calloc(1, sizeof(char) * 160);
char *curr = hex_fingeprint;
unsigned int i = 0;
for(i = 0; i < size; i++) {
sprintf(curr, "%.2X:", fingerprint[i]);
curr += 3;
}
*(curr-1) = '\0';
return hex_fingeprint;
}