ECDH 曲线 25519 密钥生成在 Libgcrypt 中的 c 实现

问题描述 投票:0回答:1

我正在尝试在 libgcrypt 中实现 ECDH。我在网上找到了如何计算共享密钥的示例,并且我知道如何进行对称加密,但我只是不知道如何生成密钥对。

我希望将密钥存储为 unsigned char *'s 。这就是我目前正在尝试执行的方法,但它总是在线上出现段错误

gcry_mpi_ec_mul(mpi_public_key, mpi_private_key, NULL, ctx);
这是我的代码:`

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);
}
c encryption ecdh libgcrypt gcrypt
1个回答
0
投票

这是 ECDH 在 C 语言 libgcrypt 中的工作实现,包括 ECC 密钥生成和共享密钥生成的单独方法。密钥存储为字符串,非对称加密主要通过 ECDH 在幕后完成。

static gpg_error_t compute_master_secret (unsigned char *master, size_t masterlen,
                       const unsigned char *sk_a, size_t sk_a_len,
                       const unsigned char *pk_b, size_t pk_b_len)
{
  gpg_error_t err;
  gcry_sexp_t s_sk_a = NULL;
  gcry_sexp_t s_pk_b = NULL;
  gcry_sexp_t s_shared = NULL;
  gcry_sexp_t s_tmp;
  const char *s;
  size_t n;
  gcry_mpi_t pk_mpi, privk_mpi;

  //Convert public key string to MPI
  err = gcry_mpi_scan(&pk_mpi,GCRYMPI_FMT_USG,pk_b,pk_b_len, NULL);
  if(err){
    printf("pk_mpi failed\n");
  }

  //Convert private key string to MPI
  err = gcry_mpi_scan(&privk_mpi,GCRYMPI_FMT_USG,sk_a,sk_a_len, NULL);
  if(err){
    printf("privk_mpi failed\n");
  }
  
  //build a private key s expression
  err = gcry_sexp_build (&s_sk_a, NULL, "%m", privk_mpi);
  if (!err)
  //build a public key s expression
    err = gcry_sexp_build (&s_pk_b, NULL,
                           "(public-key(ecdh(curve Curve25519)"
                           "(q%m)))", pk_mpi);

  if (err){
      printf ("error building S-expression: %s\n", gpg_strerror (err));
      goto leave;
    }
    // show_sexp("ecdh secret key ",s_sk_a);
    // show_sexp("ecdh pub key ",s_pk_b);

  //Encrypt secret k with public key, this does ECDH under the hood
  //Ultimately uses ecc.encrypt_raw() in cipher/ecc.c
  err = gcry_pk_encrypt (&s_shared, s_sk_a, s_pk_b);

  if (err){
      printf ("error computing DH: %s\n", gpg_strerror (err));
      goto leave;
    }

  //temp s expression to just get shared key element
  s_tmp = gcry_sexp_find_token (s_shared, "s", 0);

  //s must be prefixed with 0x40
  if (!s_tmp || !(s = gcry_sexp_nth_data (s_tmp, 1, &n))
      || n != 33 || s[0] != 0x40)
    {
      err = gpg_error (GPG_ERR_INTERNAL);
      printf ("error computing DH: %s\n", gpg_strerror (err));
      goto leave;
    }

  //plus one because the first byte is always 0x40 which denotes that it is a compressed point
  memcpy (master, s+1, 32);


 leave:
  gcry_sexp_release (s_sk_a);
  gcry_sexp_release (s_pk_b);
  gcry_sexp_release (s_shared);
  return err;

}

void crypto_box_keypair(char *public_key, char *private_key){

    gcry_error_t error;
    gcry_sexp_t key_param_sexp, key_pair_sexp;
    size_t q_len, d_len;
    char *public_key_str, *private_key_str;
    gcry_mpi_t q_mpi, d_mpi;
    unsigned char raw_public_key[crypto_box_PUBLICKEYBYTES * 2];
    unsigned char raw_private_key[crypto_box_SECRETKEYBYTES * 2];

    // Initialize libgcrypt
    if (!gcry_check_version(GCRYPT_VERSION)) {
        fprintf(stderr, "libgcrypt version mismatch\n");
        exit(EXIT_FAILURE);
    }
    gcry_control(GCRYCTL_DISABLE_SECMEM, 0);
    gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);

    //build parameters s expression
    error = gcry_sexp_build(&key_param_sexp, NULL, "(genkey (ecc (curve Curve25519)))");

    if (error) {
        fprintf(stderr, "Failed to build S-expression for key generation: %s\n", gcry_strerror(error));
        exit(EXIT_FAILURE);
    }

    //generate ecc keys
    error = gcry_pk_genkey(&key_pair_sexp, key_param_sexp);
    if (error) {
        fprintf(stderr, "Failed to generate key pair: %s\n", gcry_strerror(error));
        gcry_sexp_release(key_param_sexp);
        exit(EXIT_FAILURE);
    }

    //get sub s expression for q and d (public and private)
    gcry_sexp_t q = gcry_sexp_find_token(key_pair_sexp, "q", 0);
    gcry_sexp_t d = gcry_sexp_find_token(key_pair_sexp, "d", 0);

    //convert s expression q and d to MPIs
    q_mpi = gcry_sexp_nth_mpi(q, 1, GCRYMPI_FMT_USG);
    d_mpi = gcry_sexp_nth_mpi(d, 1, GCRYMPI_FMT_USG);

    if (!q_mpi || !d_mpi) {
        fprintf(stderr, "Failed to extract MPIs from S-expression\n");
        gcry_sexp_release(key_pair_sexp);
        exit(EXIT_FAILURE);
    }

    // Convert the MPIs to binary format
    error = gcry_mpi_print(GCRYMPI_FMT_USG, raw_public_key, sizeof(raw_public_key), &q_len, q_mpi);
    if (error) {
        fprintf(stderr, "Failed to convert public key MPI to binary format: %s\n", gcry_strerror(error));
    }

    error = gcry_mpi_print(GCRYMPI_FMT_USG, raw_private_key, sizeof(raw_private_key), &d_len, d_mpi);
    if (error) {
        fprintf(stderr, "Failed to convert private key MPI to binary format: %s\n", gcry_strerror(error));
    }

    // Remove the 0x40 prefix from the public key, if present
    if (q_len == crypto_box_PUBLICKEYBYTES + 1 && raw_public_key[0] == 0x40) {
        printf("public key is 33 in length\n");
        memcpy(public_key, raw_public_key + 1, crypto_box_PUBLICKEYBYTES);
    } else {
        printf("public key is 32 in length\n");
        memcpy(public_key, raw_public_key, crypto_box_PUBLICKEYBYTES);
    }

    memcpy(private_key, raw_private_key, crypto_box_SECRETKEYBYTES);
}

int crypto_box_easy(unsigned char *c, const unsigned char *m, unsigned long long mlen,
                const unsigned char *n, const unsigned char *pk, const unsigned char *sk){
                    
    //calculate shared key based on senders secret key, recipients public key
    char shared_key[crypto_secretbox_KEYBYTES];
    compute_master_secret(shared_key, crypto_box_SECRETKEYBYTES, sk, crypto_box_SECRETKEYBYTES, pk, crypto_box_PUBLICKEYBYTES);

    // size_t size_length = (size_t)mlen; // Cast to size_t
    // size_t *ciphertext_len = &size_length;
    //symmetric encryption with that shared key 

    if(crypto_secretbox_easy(c, m, mlen, n, shared_key) != 0){
        return -1;
    }

    return 0;
}

int main(){
    unsigned char alice_publickey[crypto_box_PUBLICKEYBYTES];
    unsigned char alice_privatekey[crypto_box_SECRETKEYBYTES];
    
    unsigned char bob_publickey[crypto_box_PUBLICKEYBYTES];
    unsigned char bob_privatekey[crypto_box_SECRETKEYBYTES];

    char *msg = "test";
    unsigned long long msg_len = 4;
    unsigned char nonce[crypto_secretbox_NONCEBYTES];

    randombytes_buf(nonce, sizeof nonce);
    
    unsigned char ciphertext[msg_len + crypto_secretbox_MACBYTES];

    unsigned char decrypted[msg_len];
    

    crypto_box_keypair(alice_publickey, alice_privatekey);
    crypto_box_keypair(bob_publickey, bob_privatekey);

    
    if (crypto_box_easy(ciphertext, msg, 6, nonce,
                        bob_publickey, alice_privatekey) != 0) {
        /* error */
    }


    if(crypto_box_open_easy(decrypted, ciphertext, CIPHERTEXT_LEN, nonce, alice_publickey, bob_privatekey) != 0){

    }
    printf("Decrypted  = %s\n", decrypted);

return 0;
}
© www.soinside.com 2019 - 2024. All rights reserved.