This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 7f9d8b0c16cc5483d3e55d906591084f40aaab40 Author: Jack Lau <[email protected]> AuthorDate: Sun Jan 11 21:56:16 2026 +0800 Commit: Gyan Doshi <[email protected]> CommitDate: Wed Jan 28 10:47:07 2026 +0000 avformat/tls_gnutls: enable dtls build Implement ff_ssl_*_key_cert() Generate self-signed cert and key in server mode if there're no key and cert input. Implement ff_tls_set_external_socket() and ff_dtls_export_materials() Add gnutls as dtls protocol deps. Signed-off-by: Jack Lau <[email protected]> --- configure | 4 +- libavformat/tls_gnutls.c | 339 +++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 341 insertions(+), 2 deletions(-) diff --git a/configure b/configure index f2e0981a60..6d6e6673dc 100755 --- a/configure +++ b/configure @@ -4002,8 +4002,8 @@ srtp_protocol_select="rtp_protocol srtp" tcp_protocol_select="network" tls_protocol_deps_any="gnutls openssl schannel securetransport libtls mbedtls" tls_protocol_select="tcp_protocol" -# TODO: Support libtls, mbedtls, and gnutls. -dtls_protocol_deps_any="openssl schannel" +# TODO: Support libtls, mbedtls. +dtls_protocol_deps_any="openssl schannel gnutls" dtls_protocol_select="udp_protocol" udp_protocol_select="network" udplite_protocol_select="network" diff --git a/libavformat/tls_gnutls.c b/libavformat/tls_gnutls.c index 36af03456f..8ac10d5a24 100644 --- a/libavformat/tls_gnutls.c +++ b/libavformat/tls_gnutls.c @@ -30,8 +30,10 @@ #include "os_support.h" #include "url.h" #include "tls.h" +#include "libavutil/intreadwrite.h" #include "libavutil/opt.h" #include "libavutil/thread.h" +#include "libavutil/random_seed.h" #ifndef GNUTLS_VERSION_NUMBER #define GNUTLS_VERSION_NUMBER LIBGNUTLS_VERSION_NUMBER @@ -42,6 +44,292 @@ GCRY_THREAD_OPTION_PTHREAD_IMPL; #endif +#define MAX_MD_SIZE 64 + +static int pkey_to_pem_string(gnutls_x509_privkey_t key, char *out, size_t out_sz) +{ + size_t required_sz = out_sz - 1; + int ret = 0; + + if (!out || !out_sz) + return AVERROR(EINVAL); + + ret = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, out, &required_sz); + if (ret < 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + av_log(NULL, AV_LOG_ERROR, + "TLS: Buffer size %zu is not enough to store private key PEM (need %zu)\n", + out_sz, required_sz + 1); + return AVERROR(EINVAL); + } + out[required_sz] = '\0'; + return required_sz; +} + +static int crt_to_pem_string(gnutls_x509_crt_t crt, char *out, size_t out_sz) +{ + size_t required_sz = out_sz - 1; + int ret = 0; + + if (!out || !out_sz) + return AVERROR(EINVAL); + + ret = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out, &required_sz); + if (ret < 0) { + if (ret == GNUTLS_E_SHORT_MEMORY_BUFFER) + av_log(NULL, AV_LOG_ERROR, + "TLS: Buffer size %zu is not enough to store certificate PEM (need %zu)\n", + out_sz, required_sz + 1); + return AVERROR(EINVAL); + } + out[required_sz] = '\0'; + return required_sz; +} + +static int gnutls_x509_fingerprint(gnutls_x509_crt_t cert, char **fingerprint) +{ + unsigned char md[MAX_MD_SIZE]; + size_t n = sizeof(md); + AVBPrint buf; + int ret; + + ret = gnutls_x509_crt_get_fingerprint(cert, GNUTLS_DIG_SHA256, md, &n); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint, %s\n", + gnutls_strerror(ret)); + return AVERROR(EINVAL); + } + + av_bprint_init(&buf, n*3, n*3); + + for (int i = 0; i < n - 1; i++) + av_bprintf(&buf, "%02X:", md[i]); + av_bprintf(&buf, "%02X", md[n - 1]); + + return av_bprint_finalize(&buf, fingerprint); +} + +int ff_ssl_read_key_cert(char *key_url, char *crt_url, char *key_buf, size_t key_sz, char *crt_buf, size_t crt_sz, char **fingerprint) +{ + int ret = 0; + AVBPrint key_bp, crt_bp; + gnutls_x509_crt_t crt = NULL; + gnutls_x509_privkey_t key = NULL; + gnutls_datum_t tmp; + + av_bprint_init(&key_bp, 1, MAX_CERTIFICATE_SIZE); + av_bprint_init(&crt_bp, 1, MAX_CERTIFICATE_SIZE); + + ret = ff_url_read_all(key_url, &key_bp); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to open key file %s\n", key_url); + goto end; + } + + ret = ff_url_read_all(crt_url, &crt_bp); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to open certificate file %s\n", crt_url); + goto end; + } + + ret = gnutls_x509_privkey_init(&key); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to init private key: %s\n", gnutls_strerror(ret)); + goto end; + } + + ret = gnutls_x509_crt_init(&crt); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to init certificate: %s\n", gnutls_strerror(ret)); + goto end; + } + + tmp.data = key_bp.str; + tmp.size = key_bp.len; + ret = gnutls_x509_privkey_import(key, &tmp, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to import private key: %s\n", gnutls_strerror(ret)); + goto end; + } + + tmp.data = crt_bp.str; + tmp.size = crt_bp.len; + ret = gnutls_x509_crt_import(crt, &tmp, GNUTLS_X509_FMT_PEM); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to import certificate: %s\n", gnutls_strerror(ret)); + goto end; + } + + ret = pkey_to_pem_string(key, key_buf, key_sz); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to converter private key to PEM string\n"); + goto end; + } + + ret = crt_to_pem_string(crt, crt_buf, crt_sz); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to converter certificate to PEM string\n"); + goto end; + } + + ret = gnutls_x509_fingerprint(crt, fingerprint); + if (ret < 0) + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint\n"); + +end: + av_bprint_finalize(&key_bp, NULL); + av_bprint_finalize(&crt_bp, NULL); + if (crt) + gnutls_x509_crt_deinit(crt); + if (key) + gnutls_x509_privkey_deinit(key); + return ret; +} + +static int gnutls_gen_private_key(gnutls_x509_privkey_t *key) +{ + int ret = 0; + + ret = gnutls_x509_privkey_init(key); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to init private key: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_privkey_generate(*key, GNUTLS_PK_ECDSA, + gnutls_sec_param_to_pk_bits(GNUTLS_PK_ECDSA, GNUTLS_SEC_PARAM_MEDIUM), 0); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate private key: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + goto end; +einval_end: + ret = AVERROR(EINVAL); + gnutls_x509_privkey_deinit(*key); + *key = NULL; +end: + return ret; +} + +static int gnutls_gen_certificate(gnutls_x509_privkey_t key, gnutls_x509_crt_t *crt, char **fingerprint) +{ + int ret = 0; + uint64_t serial; + unsigned char buf[8]; + const char *dn = "CN=lavf"; + + ret = gnutls_x509_crt_init(crt); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to init certificate: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_set_version(*crt, 3); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set certificate version: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + /** + * See https://gnutls.org/manual/gnutls.html#gnutls_005fx509_005fcrt_005fset_005fserial-1 + * The provided serial should be a big-endian positive number (i.e. its leftmost bit should be zero). + */ + serial = av_get_random_seed(); + AV_WB64(buf, serial); + buf[0] &= 0x7F; + ret = gnutls_x509_crt_set_serial(*crt, buf, sizeof(buf)); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set certificate serial: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_set_activation_time(*crt, time(NULL)); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set certificate activation time: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_set_expiration_time(*crt, time(NULL) + 365 * 24 * 60 * 60); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set certificate expiration time: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_set_dn(*crt, dn, NULL); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set certificate dn: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_set_issuer_dn(*crt, dn, NULL); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set certificate issuer dn: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_set_key(*crt, key); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to set key: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_crt_sign2(*crt, *crt, key, GNUTLS_DIG_SHA256, 0); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to sign certificate: %s\n", gnutls_strerror(ret)); + goto einval_end; + } + + ret = gnutls_x509_fingerprint(*crt, fingerprint); + if (ret < 0) + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint\n"); + + goto end; +einval_end: + ret = AVERROR(EINVAL); + gnutls_x509_crt_deinit(*crt); + *crt = NULL; +end: + return ret; +} + +int ff_ssl_gen_key_cert(char *key_buf, size_t key_sz, char *cert_buf, size_t cert_sz, char **fingerprint) +{ + int ret; + gnutls_x509_crt_t crt = NULL; + gnutls_x509_privkey_t key = NULL; + + ret = gnutls_gen_private_key(&key); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate private key\n"); + goto end; + } + + ret = gnutls_gen_certificate(key, &crt, fingerprint); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate certificate\n"); + goto end; + } + + ret = pkey_to_pem_string(key, key_buf, key_sz); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to convert private key to PEM string\n"); + goto end; + } + + ret = crt_to_pem_string(crt, cert_buf, cert_sz); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to convert certificate to PEM string\n"); + goto end; + } +end: + if (crt) + gnutls_x509_crt_deinit(crt); + if (key) + gnutls_x509_privkey_deinit(key); + return ret; +} + typedef struct TLSContext { TLSShared tls_shared; gnutls_session_t session; @@ -72,6 +360,32 @@ void ff_gnutls_deinit(void) ff_mutex_unlock(&gnutls_mutex); } +int ff_tls_set_external_socket(URLContext *h, URLContext *sock) +{ + TLSContext *c = h->priv_data; + TLSShared *s = &c->tls_shared; + + if (s->is_dtls) + s->udp = sock; + else + s->tcp = sock; + + return 0; +} + +int ff_dtls_export_materials(URLContext *h, char *dtls_srtp_materials, size_t materials_sz) +{ + int ret = 0; + TLSContext *c = h->priv_data; + + ret = gnutls_srtp_get_keys(c->session, dtls_srtp_materials, materials_sz, NULL, NULL, NULL, NULL); + if (ret < 0) { + av_log(c, AV_LOG_ERROR, "Failed to export SRTP material: %s\n", gnutls_strerror(ret)); + return -1; + } + return 0; +} + static int print_tls_error(URLContext *h, int ret) { TLSContext *c = h->priv_data; @@ -215,6 +529,8 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op TLSContext *c = h->priv_data; TLSShared *s = &c->tls_shared; uint16_t gnutls_flags = 0; + gnutls_x509_crt_t cert = NULL; + gnutls_x509_privkey_t pkey = NULL; int ret; ff_gnutls_init(); @@ -257,6 +573,25 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op } } else if (s->cert_file || s->key_file) av_log(h, AV_LOG_ERROR, "cert and key required\n"); + + if (s->listen && !s->cert_file && !s->cert_buf && !s->key_file && !s->key_buf) { + av_log(h, AV_LOG_VERBOSE, "No server certificate provided, using self-signed\n"); + + ret = gnutls_gen_private_key(&pkey); + if (ret < 0) + goto fail; + + ret = gnutls_gen_certificate(pkey, &cert, NULL); + if (ret < 0) + goto fail; + + ret = gnutls_certificate_set_x509_key(c->cred, &cert, 1, pkey); + if (ret < 0) { + av_log(h, AV_LOG_ERROR, "Unable to set self-signed certificate: %s\n", gnutls_strerror(ret)); + ret = AVERROR(EINVAL); + goto fail; + } + } gnutls_credentials_set(c->session, GNUTLS_CRD_CERTIFICATE, c->cred); gnutls_transport_set_pull_function(c->session, gnutls_url_pull); gnutls_transport_set_push_function(c->session, gnutls_url_push); @@ -306,6 +641,10 @@ static int tls_open(URLContext *h, const char *uri, int flags, AVDictionary **op return 0; fail: + if (cert) + gnutls_x509_crt_deinit(cert); + if (pkey) + gnutls_x509_privkey_deinit(pkey); tls_close(h); return ret; } _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
