mirror of
https://github.com/pion/webrtc.git
synced 2025-10-05 23:26:58 +08:00
362 lines
8.5 KiB
C
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;
|
|
}
|