You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Zephyr Socket API结合MbedTLS实现DTLS及自定义PSK回调问题

Zephyr Socket + MbedTLS DTLS 自定义PSK回调与UDP BIO实现方案

核心需求

基于Zephyr 3.5.99,结合Zephyr Socket API与MbedTLS实现DTLS功能,核心要求是通过自定义回调函数在DTLS握手阶段动态修改PSK ID。此前使用Zephyr内置的DTLS套接字(zsock_socket(af, SOCK_DGRAM, IPPROTO_DTLS_1_2))无法支持自定义回调,因此改为手动对接UDP套接字与MbedTLS,但遇到握手失败问题。

现有问题

手动对接代码运行时出现握手失败,错误码-28928(对应MBEDTLS_ERR_SSL_BAD_INPUT_DATA)。核心原因是UDP为无连接协议,原BIO收发函数使用zsock_send/zsock_recv无法处理目标地址信息,必须改用zsock_sendto/zsock_recvfrom;同时缺少UDP套接字绑定步骤,且未实现自定义PSK回调逻辑。

解决方案

1. 自定义BIO上下文结构体

将UDP套接字、目标地址、地址长度封装为结构体,作为MbedTLS BIO的上下文参数,让收发函数能获取到地址信息:

typedef struct {
    int sock_fd;
    struct sockaddr_storage dest_addr;
    socklen_t dest_addr_len;
} dtls_bio_ctx_t;

2. 实现支持地址传递的BIO收发函数

替换原有的mbedtls_net_send/mbedtls_net_recv,改用zsock_sendto/zsock_recvfrom:

int dtls_bio_send(void *ctx, const unsigned char *buf, size_t len) {
    dtls_bio_ctx_t *bio_ctx = (dtls_bio_ctx_t *)ctx;
    return zsock_sendto(bio_ctx->sock_fd, buf, len, 0,
                        (struct sockaddr *)&bio_ctx->dest_addr,
                        bio_ctx->dest_addr_len);
}

int dtls_bio_recv(void *ctx, unsigned char *buf, size_t len) {
    dtls_bio_ctx_t *bio_ctx = (dtls_bio_ctx_t *)ctx;
    return zsock_recvfrom(bio_ctx->sock_fd, buf, len, 0,
                          (struct sockaddr *)&bio_ctx->dest_addr,
                          &bio_ctx->dest_addr_len);
}

3. 实现自定义PSK回调函数

通过mbedtls_ssl_conf_psk_cb注册回调,在握手时动态修改PSK ID:

int psk_callback(void *ctx, mbedtls_ssl_context *ssl,
                 const unsigned char *hint, size_t hint_len,
                 unsigned char *psk_id, size_t *psk_id_len,
                 unsigned char *psk, size_t *psk_len) {
    // 此处可根据业务逻辑动态生成或修改PSK ID
    const unsigned char custom_psk_id[] = "Dynamic_PSK_Identity";
    const unsigned char custom_psk[] = {0x0f, 0x0e, 0x0d, 0x0c, 0x0b, 0x0a, 0x09, 0x08,
                                        0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x01};

    if (*psk_id_len < sizeof(custom_psk_id) - 1) {
        return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL;
    }
    memcpy(psk_id, custom_psk_id, sizeof(custom_psk_id) - 1);
    *psk_id_len = sizeof(custom_psk_id) - 1;

    if (*psk_len < sizeof(custom_psk)) {
        return MBEDTLS_ERR_SSL_BUFFER_TOO_SMALL;
    }
    memcpy(psk, custom_psk, sizeof(custom_psk));
    *psk_len = sizeof(custom_psk);

    return 0;
}

4. 修正主流程代码

添加UDP套接字绑定,初始化自定义BIO上下文,注册PSK回调并完成DTLS握手:

mbedtls_ssl_context ssl;
mbedtls_ssl_config conf;
dtls_bio_ctx_t bio_ctx;
int ret;

mbedtls_ssl_init(&ssl);
mbedtls_ssl_config_init(&conf);

// 配置DTLS参数(服务器角色)
if (mbedtls_ssl_config_defaults(&conf,
                                MBEDTLS_SSL_IS_SERVER,
                                MBEDTLS_SSL_TRANSPORT_DATAGRAM,
                                MBEDTLS_SSL_PRESET_DEFAULT) != 0) {
    printf("Failed to configure SSL\n");
    return -1;
}

// 创建并绑定UDP套接字
bio_ctx.sock_fd = zsock_socket(AF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (bio_ctx.sock_fd < 0) {
    printf("Failed to create UDP socket\n");
    goto end;
}

struct sockaddr_in6 server_addr = {0};
server_addr.sin6_family = AF_INET6;
server_addr.sin6_port = htons(4433);
server_addr.sin6_addr = in6addr_any;

if (zsock_bind(bio_ctx.sock_fd, (struct sockaddr *)&server_addr, sizeof(server_addr)) != 0) {
    printf("Failed to bind UDP socket\n");
    goto end;
}

// 注册自定义PSK回调
mbedtls_ssl_conf_psk_cb(&conf, psk_callback, NULL);

// 绑定自定义BIO上下文
mbedtls_ssl_set_bio(&ssl, &bio_ctx, dtls_bio_send, dtls_bio_recv, NULL);

if (mbedtls_ssl_setup(&ssl, &conf) != 0) {
    printf("Failed to set up SSL\n");
    goto end;
}

// 执行DTLS握手(UDP需先接收客户端初始报文,获取目标地址)
unsigned char recv_buf[1024];
ret = zsock_recvfrom(bio_ctx.sock_fd, recv_buf, sizeof(recv_buf), 0,
                     (struct sockaddr *)&bio_ctx.dest_addr,
                     &bio_ctx.dest_addr_len);
if (ret <= 0) {
    printf("Failed to receive client initial message\n");
    goto end;
}

// 将接收到的初始报文喂给MbedTLS
ret = mbedtls_ssl_input(&ssl, recv_buf, ret);
if (ret != 0) {
    printf("Failed to process input: %d\n", ret);
    goto end;
}

// 完成握手
ret = mbedtls_ssl_handshake(&ssl);
if (ret != 0) {
    printf("Handshake failed: %d\n", ret);
    goto end;
}

printf("DTLS handshake succeeded\n");

end:
mbedtls_ssl_free(&ssl);
mbedtls_ssl_config_free(&conf);
zsock_close(bio_ctx.sock_fd);

关键说明

  • UDP无连接特性:服务器端需先通过zsock_recvfrom获取客户端地址,再执行握手;客户端则需先设置目标服务器地址再调用握手。
  • PSK回调时机:回调会在握手阶段被MbedTLS触发,可根据hint参数或上下文信息动态生成PSK ID和密钥。
  • 错误码排查:若仍出现MBEDTLS_ERR_SSL_BAD_INPUT_DATA,可启用MbedTLS调试日志(配置CONFIG_MBEDTLS_DEBUG)查看更详细的错误原因。

内容的提问来源于stack exchange,提问作者yeetAE13

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.15 10:13:19