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]

Reply via email to