Сбой OpenSSL i2d_ECPrivateKey()

#c #openssl #elliptic-curve

#c #openssl #эллиптическая кривая

Вопрос:

У меня есть следующая программа. Он успешно получает необработанные 32-байтовые данные закрытого ключа EC, а затем создает из них EC_KEY. Но i2d_ECPrivateKey не может указать размер закрытого ключа, закодированного в DER, поскольку происходит сбой. Кто-нибудь знает, почему и как это исправить?

 #include "CBWIF.h"
#include <openssl/ssl.h>

int main(int argc, char * argv[]) {

    CBWIF wif;

    if (argc != 2)
        return EXIT_FAILURE;

    // Decode WIF string
    CBByteArray str;
    CBInitByteArrayFromString(amp;str, argv[1], false);
    CBInitWIFFromString(amp;wif, amp;str, false);
    CBDestroyByteArray(amp;str);

    // Get key
    uint8_t key[32];
    CBWIFGetPrivateKey(amp;wif, key);
    CBDestroyWIF(amp;wif);

    // Create OpenSSL key

    EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
    BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);
    if (!EC_KEY_set_private_key(eckey, bn)) 
        return EXIT_FAILURE;

    // Convert key to DER format

    int len = i2d_ECPrivateKey(eckey, NULL); // <-- CRASH HERE  
    unsigned char derkey[len];
    i2d_ECPrivateKey(eckey, (unsigned char **)amp;derkey);

    EC_KEY_free(eckey);

    // Encode DER key as hex

    char out[len*2 1];  
    CBBytesToString(derkey, 0, len, out, false);

    // Print to stdout      
    puts(out);

    return EXIT_SUCCESS;
}
  

CB_PRIVKEY_SIZE равно 32. Я проверил, что ключевые данные из CBWIFGetPrivateKey верны. Программа завершает работу со следующей трассировкой стека:

 #0  0x00007ffff766cb03 in EC_POINT_point2oct () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#1  0x00007ffff7658124 in i2d_ECPrivateKey () from /usr/lib/x86_64-linux-gnu/libcrypto.so.1.0.0
#2  0x0000000000400bf6 in main (argc=2, argv=0x7fffffffe038) at examples/WIF2DER.c:46
  

Комментарии:

1. Я только что наткнулся на это из проекта Vahalla от OpenBSD: Избегайте разыменования NULL в i2d_ECPrivateKey() , когда в EC_KEY отсутствует элемент открытого ключа .

Ответ №1:

Причина в том, что вы не можете запустить i2d_ECPrivateKey без установки открытого ключа, вероятно, из-за ошибки в OpenSSL. Я сгенерировал открытый ключ и решил несколько других проблем, и теперь программа работает:

 #include "CBWIF.h"
#include <openssl/ssl.h>

int main(int argc, char * argv[]) {

    CBWIF wif;

    if (argc != 2)
        return EXIT_FAILURE;

    // Decode WIF string
    CBByteArray str;
    CBInitByteArrayFromString(amp;str, argv[1], false);
    CBInitWIFFromString(amp;wif, amp;str, false);
    CBDestroyByteArray(amp;str);

    // Get key
    uint8_t key[32];
    CBWIFGetPrivateKey(amp;wif, key);
    CBDestroyWIF(amp;wif);

    // Create OpenSSL key
    EC_KEY * eckey = EC_KEY_new_by_curve_name(NID_secp256k1);
    BIGNUM * bn = BN_bin2bn(key, CB_PRIVKEY_SIZE, NULL);

    if (!EC_KEY_set_private_key(eckey, bn)) 
        return EXIT_FAILURE;

    // Create public key as OpenSSL cannot do this easily
    EC_GROUP * group = EC_GROUP_new_by_curve_name(NID_secp256k1);
    EC_POINT * point = EC_POINT_new(group);
    BN_CTX * ctx = BN_CTX_new();

    EC_POINT_mul(group, point, bn, NULL, NULL, ctx);

    BN_CTX_free(ctx);
    EC_GROUP_free(group);
    BN_free(bn);

    if (!EC_KEY_set_public_key(eckey, point))
        return EXIT_FAILURE;

    EC_POINT_free(point);

    // Check the key
    if (!EC_KEY_check_key(eckey))
        return EXIT_FAILURE;

    // Convert key to DER format
    int len = i2d_ECPrivateKey(eckey, NULL);    
    unsigned char derkey[len];
    unsigned char * derkeyPtr = derkey;
    i2d_ECPrivateKey(eckey, amp;derkeyPtr);

    // Freeing the EC_KEY here crashes for some reason???

    // Encode DER key as hex

    char out[len*2 1];  
    CBBytesToString(derkey, 0, len, out, false);

    // Print to stdout
    puts(out);

    return EXIT_SUCCESS;
}
  

Ответ №2:

Вот как OpenSSL использует это в <openssl src/crypto/ec/ec_ameth.c . Все другие аналогичные варианты использования в библиотеке используют i2d_ECPrivateKey_bio . Кроме того, вы могли бы быстро взглянуть на то, как OPENSSL_EC_NAMED_CURVE флаг используется с V_ASN1_OBJECT .

 unsigned char   *ep, *p;
int         eplen, ptype;
unsigned int    tmp_flags, old_flags;
...

old_flags = EC_KEY_get_enc_flags(ec_key);
tmp_flags = old_flags | EC_PKEY_NO_PARAMETERS;
...

eplen = i2d_ECPrivateKey(ec_key, NULL);
if (!eplen)
{
    EC_KEY_set_enc_flags(ec_key, old_flags);
    ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
    return 0;
}
ep = (unsigned char *) OPENSSL_malloc(eplen);
if (!ep)
{
    EC_KEY_set_enc_flags(ec_key, old_flags);
    ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_MALLOC_FAILURE);
    return 0;
}
p = ep;
if (!i2d_ECPrivateKey(ec_key, amp;p))
{
    EC_KEY_set_enc_flags(ec_key, old_flags);
    OPENSSL_free(ep);
    ECerr(EC_F_ECKEY_PRIV_ENCODE, ERR_R_EC_LIB);
    return 0;
}
...
  

Также обратите внимание на предотвращение разыменования NULL в i2d_ECPrivateKey() случае, когда в EC_KEY отсутствует элемент открытого ключа из проекта LibreSSL OpenBSD.