From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from ffbox0-bg.ffmpeg.org (ffbox0-bg.ffmpeg.org [79.124.17.100]) by master.gitmailbox.com (Postfix) with ESMTPS id 4188F400B2 for ; Sun, 11 Jan 2026 14:14:46 +0000 (UTC) Authentication-Results: ffbox; dkim=fail (body hash mismatch (got b'uFwaOqnqNeLHyV2gkcLdFEasL8DpoyvmVsroxJFd1Cw=', expected b'r9IjNI3BtPIjS5zAN9tBEfn6/d7maze3/STPxfsj/s8=')) header.d=ffmpeg.org header.i=@ffmpeg.org header.a=rsa-sha256 DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1768140873; h=mime-version : to : date : message-id : reply-to : subject : list-id : list-archive : list-archive : list-help : list-owner : list-post : list-subscribe : list-unsubscribe : from : cc : content-type : content-transfer-encoding : from; bh=uFwaOqnqNeLHyV2gkcLdFEasL8DpoyvmVsroxJFd1Cw=; b=FsS0Ye+VSlf1sfcINaXcpZ3zW9cOAkaj6MtckcAB5Zc6NGvRyVjSUUDhn3fHe9Nlc1fSf jpfxKCYJjN7I4V9A1LxL61Dkj+FgKEx3hPSwLescKWg00Qxap8IvMd9435vMSg5S+boldz5 wsuj/V+VhIa7pJwTnz7W2qx1GIV4rHVVaqo8Q6GI/xnj5kqssqsKm043Pt+HWQ9Tmfl87RC nTGMgEshl37iJyxMV4jbfYZTZZW644QzmnedI0hCz9uv+od6T98ODzzAw783nWZ4ZO6Km/l qg1P7xpihsTJjslz+9RidP2xuiF52GDCO2IlNaj5q0zoaWVtGz6mplyPeFlg== Received: from [172.20.0.4] (unknown [172.20.0.4]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTP id 8141C690E1A; Sun, 11 Jan 2026 16:14:33 +0200 (EET) ARC-Seal: i=1; cv=none; a=rsa-sha256; d=ffmpeg.org; s=arc; t=1768140857; b=D4I66+Ly3d5kUr8Z3xH+0xf/JQlLuqU8lW3+764TQF4re/GOwNAsqbQVR00MyTaxaFL4w Ni1EmovOoSuWhejo5XMBnx6XNxXbS37FFDx44PxC1a5kk36cGhAQCJrlfTNlHAxoa06CAFX lGADHCYVNLwFemBdBysI02sLlslCHABVXl/QJp2m3YPxO8r9E0vTr8CB7N2U6RreGw9V7kI hbfFRD/fXEesDgAjJuEGWweTsQU2zoWRasRDe/LkIaAWuYO+R1/wOCccPl12QEK8vQLCL8O 166oDJ9/+yNBkL0LAt3C8Ff6tbfa8rVqHCNvo9F53ZbP2AXCsvSD0F7wiLJA== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=ffmpeg.org; s=arc; t=1768140857; h=from : sender : reply-to : subject : date : message-id : to : cc : mime-version : content-type : content-transfer-encoding : content-id : content-description : resent-date : resent-from : resent-sender : resent-to : resent-cc : resent-message-id : in-reply-to : references : list-id : list-help : list-unsubscribe : list-subscribe : list-post : list-owner : list-archive; bh=9SirHeXniQa5z7sILaD++BeP/NrYcH7BFzrhJTbRWI8=; b=E0WxpcQlQsb9G7CT+03QrD2QSB6jGc0kpkHeRIT9w1PH08DXNC61cNuwNqIigiSpzg6LF 3YNcL6Dk9G90xaeM040uDSfvU5fu8fNwQoYNa7k1WYlbPT/0JFZK04viYK3YsL6bWTVXjDN 18ucpLjoYat5Knx0xZ2ZvitIvTlPHvTokw2DpJD+EjODW8TIZA4J0qIWILJYTbyRRDUXio4 BBpIBgex+3jhTtA5StGeC30rMgyYnpu4usxFz1Ko3+1S9DkhbIwrW8/vRLAy2ZrNkfqhaHy wYXN7cPlfWDDbgWXHklHVw3I5m6PBAcvdlR8s2fi+ucG8t3FxGWKucXh79JA== ARC-Authentication-Results: i=1; ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none; dmarc=pass header.from=ffmpeg.org policy.dmarc=quarantine Authentication-Results: ffmpeg.org; dkim=pass header.d=ffmpeg.org header.i=@ffmpeg.org; arc=none (Message is not ARC signed); dmarc=pass (Used From Domain Record) header.from=ffmpeg.org policy.dmarc=quarantine DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/simple; d=ffmpeg.org; i=@ffmpeg.org; q=dns/txt; s=mail; t=1768140850; h=content-type : mime-version : content-transfer-encoding : from : to : reply-to : subject : date : from; bh=r9IjNI3BtPIjS5zAN9tBEfn6/d7maze3/STPxfsj/s8=; b=BYVmB6OBF+iHipwmYe7FdHtfy28Zjk/rh8GdLQEYjO4LjNUHBAnNJjfOmYJg0YBmzhsb8 P9DwSd3J/yxcCah1Ljfp9OF5qyFDI8KbFziDbYxUz33URAP64x9nuKe2/fgPyXsG3baZ08K fQDHYwkjcnh/w2Ond9NZeMWya346CjsThmQDblk4/BH9MwvPbwzohkWEjFYi2aDL+XTZzLx 2kA/bzOXHeJked1HIXmRyMrIVW0OHZxmf1WZmo1mtYyTqRbHr05U5rf2Tvp4FLuiEiyMd8i E9kXIg9IjmBqrvB1nqwajgZjVHaXB/zRrIFgK8laGC1Ju7e3o6+jZOjd5kYg== Received: from f7c34508609e (code.ffmpeg.org [188.245.149.3]) by ffbox0-bg.ffmpeg.org (Postfix) with ESMTPS id 523026903AD for ; Sun, 11 Jan 2026 16:14:10 +0200 (EET) MIME-Version: 1.0 To: ffmpeg-devel@ffmpeg.org Date: Sun, 11 Jan 2026 14:14:10 -0000 Message-ID: <176814085050.25.16376540336211401978@4457048688e7> Message-ID-Hash: TWGW7TS4I5Y7YWK5SLIS5XD6M2YNELVQ X-Message-ID-Hash: TWGW7TS4I5Y7YWK5SLIS5XD6M2YNELVQ X-MailFrom: code@ffmpeg.org X-Mailman-Rule-Hits: nonmember-moderation X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; loop; banned-address; header-match-ffmpeg-devel.ffmpeg.org-0; header-match-ffmpeg-devel.ffmpeg.org-1; header-match-ffmpeg-devel.ffmpeg.org-2; header-match-ffmpeg-devel.ffmpeg.org-3; emergency; member-moderation X-Mailman-Version: 3.3.10 Precedence: list Reply-To: FFmpeg development discussions and patches Subject: [FFmpeg-devel] [PR] avformat/tls_gnutls: implement ff_ssl_gen_key_cert() (PR #21432) List-Id: FFmpeg development discussions and patches Archived-At: Archived-At: List-Archive: List-Archive: List-Help: List-Owner: List-Post: List-Subscribe: List-Unsubscribe: From: Jack Lau via ffmpeg-devel Cc: Jack Lau Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit Archived-At: List-Archive: List-Post: PR #21432 opened by Jack Lau (JackLau) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21432 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21432.patch Generate self-signed cert and key in server mode if there're no key and cert input. Signed-off-by: Jack Lau >>From 26ddef616a5d7ec5a425cbcd36378ea2d7e710f8 Mon Sep 17 00:00:00 2001 From: Jack Lau Date: Sun, 11 Jan 2026 21:56:16 +0800 Subject: [PATCH] avformat/tls_gnutls: implement ff_ssl_gen_key_cert() Generate self-signed cert and key in server mode if there're no key and cert input. Signed-off-by: Jack Lau --- libavformat/tls_gnutls.c | 237 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 237 insertions(+) diff --git a/libavformat/tls_gnutls.c b/libavformat/tls_gnutls.c index 6f58ec03d2..ccd2e261d6 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,219 @@ 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 read_bytes = 0; + int ret = 0; + + if (!out || !out_sz) + return AVERROR(EINVAL); + + gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, NULL, &read_bytes); + if (!read_bytes) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to read the private key size\n"); + return AVERROR(EINVAL); + } + + if (read_bytes > out_sz) { + av_log(NULL, AV_LOG_ERROR, "TLS: Private key PEM string size %zu is large than %zu\n", read_bytes, out_sz); + return AVERROR(EINVAL); + } + + ret = gnutls_x509_privkey_export(key, GNUTLS_X509_FMT_PEM, out, &read_bytes); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to export private key: %s\n", gnutls_strerror(ret)); + return AVERROR(EINVAL); + } + out[read_bytes] = '\0'; + return read_bytes; +} + +static int crt_to_pem_string(gnutls_x509_crt_t crt, char *out, size_t out_sz) +{ + size_t read_bytes = 0; + int ret = 0; + + if (!out || !out_sz) + return AVERROR(EINVAL); + + gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, NULL, &read_bytes); + if (!read_bytes) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to read the certificate size\n"); + return AVERROR(EINVAL); + } + + if (read_bytes > out_sz) { + av_log(NULL, AV_LOG_ERROR, "TLS: Certificate PEM string size %zu is large than %zu\n", read_bytes, out_sz); + return AVERROR(EINVAL); + } + + ret = gnutls_x509_crt_export(crt, GNUTLS_X509_FMT_PEM, out, &read_bytes); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to export certificate: %s\n", gnutls_strerror(ret)); + return AVERROR(EINVAL); + } + out[read_bytes] = '\0'; + return read_bytes; +} + +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(ENOMEM); + } + + 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); +} + +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 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 end; + } +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 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 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 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 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 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 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 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 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 end; + } + + ret = gnutls_x509_fingerprint(*crt, fingerprint); + if (ret < 0) + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate fingerprint\n"); +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: %s\n", gnutls_strerror(ret)); + goto end; + } + + ret = gnutls_gen_certificate(key, &crt, fingerprint); + if (ret < 0) { + av_log(NULL, AV_LOG_ERROR, "TLS: Failed to generate 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 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; @@ -263,6 +478,28 @@ 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) { + gnutls_x509_crt_t cert = NULL; + gnutls_x509_privkey_t pkey = NULL; + + 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); -- 2.49.1 _______________________________________________ ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org