5

I am trying to compute public key from given secret by openssl. I get this error:

main.c:27: error: incomplete definition of type 'struct ec_key_st'
  printf("d: %s\n", BN_bn2hex(eckey->priv_key));
                              ~~~~~^

Here is my code:

#include <stdio.h>

#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/bn.h>
#include <openssl/obj_mac.h>

int main()
{
  BN_CTX *ctx = BN_CTX_new();

  EC_KEY *eckey = EC_KEY_new();
  EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1);
  EC_KEY_set_group(eckey, group);

  BIGNUM *prv = BN_new();
  BN_hex2bn(&prv, "b14fac12b3fa7dd6f2562a18d554fcd6818137ebb7e0d119ab0776d6407664f9");
  EC_KEY_set_private_key(eckey, prv);

  EC_POINT *Q = EC_POINT_new(group);
  EC_POINT_mul(group, Q, prv, NULL, NULL, ctx);
  EC_KEY_set_public_key(eckey, Q);

  if (EC_KEY_check_key(eckey))
    printf("Key succesfully checked.\n");

  printf("d: %s\n", BN_bn2hex(eckey->priv_key));
  printf("X: %s\n", BN_bn2hex(&eckey->pub_key->X));
  printf("Y: %s\n", BN_bn2hex(&eckey->pub_key->Y));

  EC_GROUP_free (group); group = NULL;
  EC_KEY_free (eckey); eckey = NULL;
  return 0;
}

What is wrong with the above code? If I remove printf lines, it works fine. I would appreciate if anybody helps me getting rid of this error.

jww
  • 97,681
  • 90
  • 411
  • 885
miaray
  • 51
  • 1
  • 4

2 Answers2

8

You're using OpenSSL 1.1, and they have decided that you shouldn't be poking about at the innards of their structures anymore.

eckey->priv_key was a valid way of accessing the private key in OpenSSL 1.0.x, but now the only correct way is EC_KEY_get0_private_key(eckey)

Likewise, for the public key it's EC_KEY_get0_public_key(eckey).

Both of these functions were declared during OpenSSL 1.0.x, so you can write the code to be the same between them.

So

printf("d: %s\n", BN_bn2hex(eckey->priv_key));
printf("X: %s\n", BN_bn2hex(&eckey->pub_key->X));
printf("Y: %s\n", BN_bn2hex(&eckey->pub_key->Y));

would become

{
    const BIGNUM* d = EC_KEY_get0_private_key(eckey);
    const EC_POINT* Q = EC_KEY_get0_public_key(eckey);
    const EC_GROUP* group = EC_KEY_get0_group(eckey);
    BIGNUM* x = BN_new();
    BIGNUM* y = BN_new();

    if (!EC_POINT_get_affine_coordinates_GFp(group, Q, x, y, null))
    {
        error();
    }

    printf("d: %s\n", BN_bn2hex(d));
    printf("X: %s\n", BN_bn2hex(x));
    printf("Y: %s\n", BN_bn2hex(y));

    BN_free(x);
    BN_free(y);
}

This keeps your code working, even when OpenSSL 1.1.1 decides to redo the struct layout which was hidden within ec_lcl.h

bartonjs
  • 30,352
  • 2
  • 71
  • 111
  • 1
    Actually EC_KEY has *always* been declared as opaque, even in 1.0.x. So accessing its internals directly was never a valid way of doing things. Apart from that, this is a good answer. – Matt Caswell Oct 04 '17 at 10:12
0

You need to get "ec_lcl.h" header file from openssl libarary, in order to access members of struct "ec_key_st". Notice that "ECKEY" is defined as:

typedef struct ec_key_st EC_KEY;

So you put "ec_lcl.h" in your project folder and then change your code to this:

#include <stdio.h>

#include <openssl/ec.h>
#include <openssl/ecdsa.h>
#include <openssl/bn.h>
#include <openssl/obj_mac.h>

#include "ec_lcl.h"

int main()
{
  BN_CTX *ctx = BN_CTX_new();

  struct ec_key_st *eckey = EC_KEY_new();
  EC_GROUP *group = EC_GROUP_new_by_curve_name(NID_secp256k1);
  EC_KEY_set_group(eckey, group);

  BIGNUM *prv = BN_new();
  BN_hex2bn(&prv, "b14fac12b3fa7dd6f2562a18d554fcd6818137ebb7e0d119ab0776d6407664f9");
  EC_KEY_set_private_key(eckey, prv);

  EC_POINT *Q = EC_POINT_new(group);
  EC_POINT_mul(group, Q, prv, NULL, NULL, ctx);
  EC_KEY_set_public_key(eckey, Q);

  if (EC_KEY_check_key(eckey))
    printf("Key succesfully checked.\n");

  printf("d: %s\n", BN_bn2hex(eckey->priv_key));
  printf("X: %s\n", BN_bn2hex(&eckey->pub_key->X));
  printf("Y: %s\n", BN_bn2hex(&eckey->pub_key->Y));

  EC_GROUP_free (group); group = NULL;
  EC_KEY_free (eckey); eckey = NULL;
  return 0;
}
hutheano
  • 172
  • 2
  • 10
  • This is not the correct way to do things. "ec_lcl.h" is an internal header file and not part of the public API. Accessing the internals of the structure directly is liable to break from one version of OpenSSL to another. There are public API functions for accessing these things which should be used instead. See the answer from @bartonjs – Matt Caswell Oct 04 '17 at 10:14
  • 2
    Is it really "not the correct way" ? "not correct" technically? socially? why? because some other ocd folks might get their feelings hurt? OpenSSL is open source library. So if some folks can read and understand its internals, they should have a right to use them as they like, at least as long as them not having any expectation of their code working with future changes to that library. – hutheano Oct 04 '17 at 11:26
  • 1
    By using ec_lcl.h you become vulnerable to problems where a field gets added or removed to a struct in the next update. Your code thinks it is at offset 16, their code thinks it's at offset 24, and now you are working with some field other than you thought. While that is true in public headers, too, it's the case that a breaking change to a public struct breaks everyone (their bug) and a change to a private one only breaks you (your bug). So, yeah, this is definitely "not the correct way". – bartonjs Oct 04 '17 at 13:11
  • Hypothetically, there is an update that changes 'struct ec_key_st'. I either continue to use compatible version of library, or update my code in order to use updated library (by simply recompiling with new "ec_lcl.h"). Limiting users of an opens source library to use only "public" headers and tools of that library, and observe given orders from them is a bit authoritarian buddy. It sucks the fun out of programming. These kinds of autocratic attitudes kills creativity in every field of science. You might like listen to this: http://www.youtube.com/watch?v=iG9CE55wbtY – hutheano Oct 04 '17 at 13:28
  • 3
    You are of course at liberty to do anything you like with an open source library. But as soon as you open the lid on its internals then you hit a whole can of worms. You are tied to a specific patch level of the library, so your code can become very brittle. That's particularly dangerous in a security library. My use of the word "correct" should be read as "the way that it was designed to be used". – Matt Caswell Oct 05 '17 at 08:49