Thank your for your reply, but do I not have to use the IO callbacks, because I have no os?

#ifndef MIDDLEWARES_WOLFSSL_WOLFSSL_WOLFCRYPT_USER_SETTINGS_H_
#define MIDDLEWARES_WOLFSSL_WOLFSSL_WOLFCRYPT_USER_SETTINGS_H_

#define WOLFSSL_SHA256
#define WOLFSSL_SHA384
#define OPENSSL_EXTRA
#define CUSTOM_RAND_GENERATE_BLOCK
#define DEBUG_WOLFSSL
#define NO_OLD_TLS
#define HAVE_ECC
#define WOLFSSL_SHA512
#define WOLFSSL_NO_SOCK
#define WOLFSSL_USER_IO
#define NO_RSA
#define HAVE_AEAD
#define HAVE_HKDF
#define HAVE_TLS_EXTENSIONS
#define NO_DH
#define HAVE_ED25519
#define HAVE_CURVE25519
#define WOLFSSL_TLS13
#define WOLFSSL_NO_TLS12
#define NO_FILESYSTEM
#define NO_STDIO
#define NO_DIR
#define SINGLE_THREADED
#define NO_WRITEV
#define NO_READV
#define NO_IOVEC
#define WOLFSSL_NO_ASM
#define HAVE_ERRNO_H
#include <errno.h>


#ifdef WOLFSSL_NO_MALLOC
  #undef WOLFSSL_NO_MALLOC
#endif




#endif /* MIDDLEWARES_WOLFSSL_WOLFSSL_WOLFCRYPT_USER_SETTINGS_H_ */

and the user settings.

#include "wolfssl/ssl.h"
#include "certs.h"
#include "lwip/tcp.h"
#include "tls_client.h"
#include <stdlib.h>


static WOLFSSL_CTX *g_ctx = NULL;


#define TLS_RX_BUF_SIZE   4096

typedef enum {
    TLS_STATE_IDLE = 0,
    TLS_STATE_TCP_CONNECTING,
    TLS_STATE_TCP_CONNECTED,
    TLS_STATE_TLS_HANDSHAKE,
    TLS_STATE_TLS_ESTABLISHED,
    TLS_STATE_ERROR
} tls_state_t;

typedef struct {
    struct tcp_pcb* pcb;
    WOLFSSL*        ssl;
    tls_state_t     state;

    uint8_t         rxBuf[TLS_RX_BUF_SIZE];
    uint32_t        rxHead;
    uint32_t        rxTail;

    int             lastErr;      // debug
} tls_conn_t;

static tls_conn_t g_tls;


//
//static void* myMalloc(size_t n) { return malloc(n); }
//static void  myFree(void* p)    { free(p); }
//static void* myRealloc(void* p, size_t n) { return realloc(p, n); }


static uint32_t tls_rx_available(tls_conn_t* c) {
    if (c->rxHead >= c->rxTail)
        return c->rxHead - c->rxTail;
    else
        return TLS_RX_BUF_SIZE - (c->rxTail - c->rxHead);
}

static uint32_t tls_rx_space(tls_conn_t* c) {
    return TLS_RX_BUF_SIZE - 1 - tls_rx_available(c);
}

static void tls_rx_push(tls_conn_t* c, const uint8_t* data, uint32_t len) {
    while (len--) {
        c->rxBuf[c->rxHead] = *data++;
        c->rxHead = (c->rxHead + 1) % TLS_RX_BUF_SIZE;
    }
}

static uint32_t tls_rx_pop(tls_conn_t* c, uint8_t* dst, uint32_t maxLen) {
    uint32_t avail = tls_rx_available(c);
    if (avail == 0) return 0;

    if (maxLen > avail) maxLen = avail;
    for (uint32_t i = 0; i < maxLen; i++) {
        dst[i] = c->rxBuf[c->rxTail];
        c->rxTail = (c->rxTail + 1) % TLS_RX_BUF_SIZE;
    }
    return maxLen;
}

static err_t tls_tcp_connected_cb(void *arg, struct tcp_pcb *tpcb, err_t err)
{
    tls_conn_t* c = (tls_conn_t*)arg;
    printf("connected_cb err=%d\r\n", (int)err);

    if (err != ERR_OK) {
        c->state = TLS_STATE_ERROR;
        return err;
    }

    c->state = TLS_STATE_TCP_CONNECTED;

    return ERR_OK;
}

static err_t tls_tcp_recv_cb(void *arg, struct tcp_pcb *tpcb,
                             struct pbuf *p, err_t err)
{
    tls_conn_t* c = (tls_conn_t*)arg;

    if (p == NULL) {
        c->state = TLS_STATE_ERROR;
        return ERR_OK;
    }

    if (tls_rx_space(c) < p->tot_len) {
        printf("RX overflow -> abort TLS\n");
        pbuf_free(p);
//        tcp_abort(tpcb);
        c->state = TLS_STATE_ERROR;
        c->lastErr = ERR_MEM;
        return ERR_ABRT;
    }

    struct pbuf* q = p;
    while (q) {
        tls_rx_push(c, (uint8_t*)q->payload, q->len);
        q = q->next;
    }

    tcp_recved(tpcb, p->tot_len);
    pbuf_free(p);
    return ERR_OK;
}





static void tls_tcp_err_cb(void *arg, err_t err)
{
    tls_conn_t* c = (tls_conn_t*)arg;
    printf("tcp_err_cb err=%d\r\n", (int)err);
    c->state   = TLS_STATE_ERROR;
    c->lastErr = err;
}


/* wird via WOLFSSL_USER_IO verwendet */
static int my_IORecv(WOLFSSL* ssl, char* buff, int sz, void* ctx)
{
    tls_conn_t* c = (tls_conn_t*)ctx;
    uint32_t n = tls_rx_pop(c, (uint8_t*)buff, (uint32_t)sz);

    if (n == 0) {
        // keine Daten gerade → nicht blocken
        return WOLFSSL_CBIO_ERR_WANT_READ;
    }

    return (int)n;
}

static int my_IOSend(WOLFSSL* ssl, char* buff, int sz, void* ctx)
{
    static int cnt = 0;
    cnt++;
    printf("IOSend #%d sz=%d\r\n", cnt, sz);


    tls_conn_t* c = (tls_conn_t*)ctx;
    if (c->pcb == NULL) return WOLFSSL_CBIO_ERR_CONN_CLOSE;

    err_t err = tcp_write(c->pcb, buff, sz, TCP_WRITE_FLAG_COPY);
    if (err == ERR_MEM) {
        return WOLFSSL_CBIO_ERR_WANT_WRITE;
    } else if (err != ERR_OK) {
        return WOLFSSL_CBIO_ERR_GENERAL;
    }

    tcp_output(c->pcb);
    return sz;
}




static void wolfssl_dump_errors(void)
{
    unsigned long e;
    char buf[WOLFSSL_MAX_ERROR_SZ];

    while ((e = wolfSSL_ERR_get_error()) != 0) {
        wolfSSL_ERR_error_string_n(e, buf, sizeof(buf));
        printf("wolfSSL: %s\n", buf);
    }
}

void test_rng(void) {
    WC_RNG rng;
    int ret = wc_InitRng(&rng);
    if (ret != 0) {
        char msg[WOLFSSL_MAX_ERROR_SZ];
        wc_ErrorString(ret, msg);
        printf("wc_InitRng failed: %d (%s)\r\n", ret, msg);
        return;
    }
    wc_FreeRng(&rng);
    printf("wc_InitRng OK\r\n");
}




void TLS_GlobalInit(void)
{
    test_rng();
    wolfSSL_Init();
    int i = 0;
    int ret;

//    wolfSSL_SetAllocators(myMalloc, myFree, myRealloc);
    g_ctx = wolfSSL_CTX_new(wolfTLSv1_3_client_method());
    wolfSSL_SetIORecv(g_ctx, my_IORecv);
    wolfSSL_SetIOSend(g_ctx, my_IOSend);
    if (g_ctx == NULL)
    {
        /* fatal – keine TLS-Konfiguration */
        while (1)
        {
            if (i == 0)
            {
        printf("leider falsch\n");
                i += 1;
            }

        }
    }

    ret = wolfSSL_CTX_load_verify_buffer(g_ctx, ca_ed25519_der,
            ca_ed25519_der_len, WOLFSSL_FILETYPE_ASN1);

//    wolfSSL_CTX_set_verify(g_ctx, WOLFSSL_VERIFY_NONE, 0);


    if (ret != WOLFSSL_SUCCESS) {
        char errBuf[80];
        /* Bei ctx-Funktionen einfach direkt den Code in einen String umwandeln: */
        wolfSSL_ERR_error_string_n((unsigned long)ret, errBuf, sizeof(errBuf));
        printf("load_verify_buffer failed: %d (%s)\n", ret, errBuf);
    }

    printf("hallihallo\n");
}




void TLS_ClientStart(const ip_addr_t* ip, u16_t port)
{
    memset(&g_tls, 0, sizeof(g_tls));

    g_tls.pcb = tcp_new();
    if (g_tls.pcb == NULL) {
        g_tls.state = TLS_STATE_ERROR;
        return;
    }

    g_tls.state = TLS_STATE_TCP_CONNECTING;

    tcp_arg(g_tls.pcb, &g_tls);      // our connection struct as arg
    tcp_err(g_tls.pcb, tls_tcp_err_cb);
    tcp_recv(g_tls.pcb, tls_tcp_recv_cb);

    err_t e = tcp_connect(g_tls.pcb, ip, port, tls_tcp_connected_cb);
    printf("tcp_connect()=%d\r\n", (int)e);
    if (e != ERR_OK) { g_tls.state = TLS_STATE_ERROR; g_tls.lastErr = e; }
}





void TLS_ClientProcess(void)
{
    int ret;

    switch (g_tls.state) {

    case TLS_STATE_TCP_CONNECTED:

        printf("g_ctx=%p\r\n", (void*)g_ctx);
        g_tls.ssl = wolfSSL_new(g_ctx);
        if (g_tls.ssl == NULL) {
            g_tls.state = TLS_STATE_ERROR;
            break;
        }

//        wolfSSL_SSLSetIORecv(g_tls.ssl, my_IORecv); //extra
//        wolfSSL_SSLSetIOSend(g_tls.ssl, my_IOSend); //etxra

        wolfSSL_SetIOReadCtx(g_tls.ssl,  &g_tls);
        wolfSSL_SetIOWriteCtx(g_tls.ssl, &g_tls);

        wolfSSL_set_using_nonblock(g_tls.ssl, 1);

        g_tls.state = TLS_STATE_TLS_HANDSHAKE;
        break;

    case TLS_STATE_TLS_HANDSHAKE:

//        const char dummy[] = "X";
//        ret = wolfSSL_write(g_tls.ssl, dummy, sizeof(dummy));
//        int err = wolfSSL_get_error(g_tls.ssl, ret);
//        printf("wolfSSL_write ret=%d err=%d\r\n", ret, err);

        ret = wolfSSL_connect(g_tls.ssl);
        if (ret == WOLFSSL_SUCCESS) {
            printf("TLS handshake OK\r\n");
            g_tls.state = TLS_STATE_TLS_ESTABLISHED;
        } else
        {
            int err = wolfSSL_get_error(g_tls.ssl, ret);
            if (err == WOLFSSL_ERROR_WANT_READ ||
                err == WOLFSSL_ERROR_WANT_WRITE) {
            } else {
                printf("TLS handshake failed: %d\r\n", err);
                g_tls.state = TLS_STATE_ERROR;



                    wolfssl_dump_errors();   // <-- zeigt den echten Grund
            }
        }
        break;

    case TLS_STATE_TLS_ESTABLISHED:
        /* hier könntest du z.B. einmalig etwas senden: */
        // const char* msg = "HELLO FROM STM32\r\n";
        // wolfSSL_write(g_tls.ssl, msg, strlen(msg));
        // g_tls.state = TLS_STATE_IDLE; // oder weitere Logik
        break;

    case TLS_STATE_ERROR:
        printf("tcp_err_cb: err=%d\r\n", g_tls.lastErr);
//        printf("TCP connect failed, err=%d\r\n", g_tls.lastErr);
        // ggf. pcb/ssl freigeben etc.
        g_tls.state = TLS_STATE_TLS_HANDSHAKE;

        break;

    default:
//        printf("TCP connect failed, err=%d\r\n", g_tls.state);
        break;
    }
}

here is the most important code

Hello There,

I have issues with achieving a Handshake, while the stm32n6570-dk acting as a Client and a Debian System as the corresponding Server.

After the TLS Handshake and initializing WolfSSL plus providing him the Root Certificate, it gives me errors at wolfssl_connect:

It returns -1.

wolfSSL_ERR_get_error says something like "unknown error".

wolfSSL_get_error just returns 32.

I am actually quite stuck at this point.