November 5, 2018 · Don't Forget c++

How to access the raw public/private tuple and params inside OpenSSL's EVP_PKEY structure?

I'm using OpenSSL's c++ library to generate a public/private tuple, following the code sample below:

EVP_PKEY* my_pkey = nullptr;
EVP_PKEY_CTX* my_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_ED25519, nullptr);
EVP_PKEY_keygen_init(my_ctx);
EVP_PKEY_keygen(my_ctx, &my_pkey);

But needed to extract the raw (uint8_t*) array of bytes. I found the following reference which suggested:

a) To serialize the public key:

Pass the EVP_PKEY to EVP_PKEY_get1_EC_KEY() to get an EC_KEY.
Pass the EC_KEY to EC_KEY_get0_public_key() to get an EC_POINT.
Pass the EC_POINT to EC_POINT_point2oct() to get octets, which are just unsigned char *.

b) To deserialize the public key:

Pass the octets to EC_POINT_oct2point() to get an EC_POINT.
Pass the EC_POINT to EC_KEY_set_public_key() to get an EC_KEY.
Pass the EC_KEY to EVP_PKEY_set1_EC_KEY to get an EVP_KEY.

c) To serialize the private key:

Pass the EVP_PKEY to EVP_PKEY_get1_EC_KEY() to get an EC_KEY.
Pass the EC_KEY to EC_KEY_get0_private_key() to get a BIGNUM.
Pass the BIGNUM to BN_bn2mpi() to get an mpi, which is a format written to unsigned char *.

d) To deserialize the private key:

Pass the mpi to BN_mpi2bn() to get a BIGNUM.
Pass the BIGNUM to EC_KEY_set_private_key() to get an EC_KEY.
Pass the EC_KEY to EVP_PKEY_set1_EC_KEY to get an EVP_KEY

However I ended up doing this using the following code snippet. Which was was committed to openssl in June (https://github.com/openssl/openssl/pull/6394):

uint8_t* pub_key = new uint8_t[32];
std::size_t allocated_length;
EVP_PKEY_get_raw_public_key(my_pkey, pub_key, &allocated_length);
ASSET_EQ(allocated_length, 32);

EVP_PKEY_get_raw_public_key() fills the buffer provided by pub with raw public key data. The number of bytes written is populated in *len. If the buffer pub is NULL then *len is populated with the number of bytes required to hold the key. The calling application is responsible for ensuring that the buffer is large enough to receive the public key data. This function only works for algorithms that support raw public keys. Currently this is: EVP_PKEY_X25519, EVP_PKEY_ED25519, EVP_PKEY_X448 or EVP_PKEY_ED448.

And there is an equivalent EVP_PKEY_get_raw_private_key() as well.