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

基于Libgcrypt实现ECDH Curve25519密钥对生成的段错误求助

libgcrypt Curve25519密钥对生成段错误修复

在libgcrypt中实现ECDH时,共享密钥计算和对称加密逻辑正常,但密钥对生成过程中,gcry_mpi_ec_mul(mpi_public_key, mpi_private_key, NULL, ctx);行始终触发段错误,目标是将密钥存储为unsigned char*类型,原代码如下:

void generate_curve25519_keypair(unsigned char *public_key, unsigned char *private_key) {
    gcry_error_t error;
    gcry_mpi_t mpi_private_key;
    gcry_mpi_point_t mpi_public_key;
    gcry_ctx_t ctx;

    // Generate a random private key
    gcry_randomize(private_key, CURVE25519_KEY_SIZE, GCRY_STRONG_RANDOM);

    // Clamp the private key for Curve25519
    private_key[0] &= 248;
    private_key[31] &= 127;
    private_key[31] |= 64;

    // Initialize the context for Curve25519
    error = gcry_mpi_ec_new(&ctx, NULL, "Curve25519");
    if (error) {
        fprintf(stderr, "Failed to create context: %s\n", gpg_strerror(error));
        return;
    }

    // Convert the private key to an MPI
    error = gcry_mpi_scan(&mpi_private_key, GCRYMPI_FMT_USG, private_key, CURVE25519_KEY_SIZE, NULL);
    if (error) {
        fprintf(stderr, "Failed to convert private key to MPI: %s\n", gpg_strerror(error));
        gcry_ctx_release(ctx);
        return;
    }

    // Allocate memory for the resulting public key point
    mpi_public_key = gcry_mpi_point_new(0);

    // Multiply the base point with the private key MPI to get the public key point
    gcry_mpi_ec_mul(mpi_public_key, mpi_private_key, NULL, ctx); // Use NULL for the default base point G
    if (error) {
        fprintf(stderr, "Failed to multiply with base point: %s\n", gpg_strerror(error));
        gcry_mpi_release(mpi_private_key);
        gcry_mpi_point_release(mpi_public_key);
        gcry_ctx_release(ctx);
        return;
    }

    // Extract the x-coordinate of the public key point and convert it to a byte array
    gcry_mpi_t mpi_x = gcry_mpi_new(0);
    gcry_mpi_point_get(mpi_x, NULL, NULL, mpi_public_key);
    error = gcry_mpi_print(GCRYMPI_FMT_USG, public_key, CURVE25519_KEY_SIZE, NULL, mpi_x);
    if (error) {
        fprintf(stderr, "Failed to export public key: %s\n", gpg_strerror(error));
        gcry_mpi_release(mpi_x);
        gcry_mpi_release(mpi_private_key);
        gcry_mpi_point_release(mpi_public_key);
        gcry_ctx_release(ctx);
        return;
    }

    // Release resources
    gcry_mpi_release(mpi_x);
    gcry_mpi_release(mpi_private_key);
    gcry_mpi_point_release(mpi_public_key);
    gcry_ctx_release(ctx);
}

错误分析

  • 未捕获gcry_mpi_ec_mul返回错误:原代码调用该函数后未将返回值赋值给error,后续错误检查完全无效,无法定位函数调用本身的问题。
  • gcry_mpi_point_new参数错误:创建公钥点时传入0,导致点对象未关联Curve25519的上下文域参数,执行乘法操作时访问未初始化内存,直接引发段错误。正确做法是传入已创建的曲线上下文ctx。
  • 内存分配无空指针检查:gcry_mpi_point_new、gcry_mpi_new等内存分配操作未检查返回值,存在潜在崩溃风险。
  • gcry_mpi_point_get未捕获错误:提取公钥坐标时未处理函数返回错误,无法定位提取失败的原因。

修正后代码

#include <gcrypt.h>
#include <stdio.h>
#include <string.h>

#define CURVE25519_KEY_SIZE 32

void generate_curve25519_keypair(unsigned char *public_key, unsigned char *private_key) {
    gcry_error_t error;
    gcry_mpi_t mpi_private_key;
    gcry_mpi_point_t mpi_public_key;
    gcry_ctx_t ctx;

    // 生成随机私钥
    gcry_randomize(private_key, CURVE25519_KEY_SIZE, GCRY_STRONG_RANDOM);

    // Curve25519私钥钳位处理
    private_key[0] &= 248;
    private_key[31] &= 127;
    private_key[31] |= 64;

    // 初始化Curve25519上下文
    error = gcry_mpi_ec_new(&ctx, NULL, "Curve25519");
    if (error) {
        fprintf(stderr, "创建上下文失败: %s\n", gpg_strerror(error));
        return;
    }

    // 将私钥转换为MPI
    error = gcry_mpi_scan(&mpi_private_key, GCRYMPI_FMT_USG, private_key, CURVE25519_KEY_SIZE, NULL);
    if (error) {
        fprintf(stderr, "私钥转MPI失败: %s\n", gpg_strerror(error));
        gcry_ctx_release(ctx);
        return;
    }

    // 关联上下文创建公钥点对象(核心修复:传入ctx而非0)
    mpi_public_key = gcry_mpi_point_new(ctx);
    if (!mpi_public_key) {
        fprintf(stderr, "公钥点内存分配失败\n");
        gcry_mpi_release(mpi_private_key);
        gcry_ctx_release(ctx);
        return;
    }

    // 私钥乘基点生成公钥,捕获返回错误
    error = gcry_mpi_ec_mul(mpi_public_key, mpi_private_key, NULL, ctx);
    if (error) {
        fprintf(stderr, "基点乘法失败: %s\n", gpg_strerror(error));
        gcry_mpi_release(mpi_private_key);
        gcry_mpi_point_release(mpi_public_key);
        gcry_ctx_release(ctx);
        return;
    }

    // 提取公钥点的x坐标
    gcry_mpi_t mpi_x = gcry_mpi_new(0);
    if (!mpi_x) {
        fprintf(stderr, "MPI内存分配失败\n");
        gcry_mpi_release(mpi_private_key);
        gcry_mpi_point_release(mpi_public_key);
        gcry_ctx_release(ctx);
        return;
    }
    error = gcry_mpi_point_get(mpi_x, NULL, NULL, mpi_public_key);
    if (error) {
        fprintf(stderr, "提取公钥x坐标失败: %s\n", gpg_strerror(error));
        gcry_mpi_release(mpi_x);
        gcry_mpi_release(mpi_private_key);
        gcry_mpi_point_release(mpi_public_key);
        gcry_ctx_release(ctx);
        return;
    }

    // 将x坐标转换为字节数组
    error = gcry_mpi_print(GCRYMPI_FMT_USG, public_key, CURVE25519_KEY_SIZE, NULL, mpi_x);
    if (error) {
        fprintf(stderr, "导出公钥失败: %s\n", gpg_strerror(error));
        gcry_mpi_release(mpi_x);
        gcry_mpi_release(mpi_private_key);
        gcry_mpi_point_release(mpi_public_key);
        gcry_ctx_release(ctx);
        return;
    }

    // 释放资源
    gcry_mpi_release(mpi_x);
    gcry_mpi_release(mpi_private_key);
    gcry_mpi_point_release(mpi_public_key);
    gcry_ctx_release(ctx);
}

关键修复说明

  • gcry_mpi_point_new(ctx):确保公钥点对象关联Curve25519的域参数,避免操作未初始化内存引发段错误。
  • 完善错误捕获:新增gcry_mpi_ec_mul、gcry_mpi_point_get等关键函数的错误检查,便于定位问题。
  • 内存分配检查:对所有内存分配操作添加空指针检查,避免潜在崩溃。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.22 00:23:10