1

i need to implement ecdh with 25519 using openssl.

using:

key = EC_KEY_new_by_curve_name(NID_X25519)

fails.

using this:

EVP_PKEY *pkey = NULL;
EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(NID_X25519, NULL);
EVP_PKEY_keygen_init(pctx);
EVP_PKEY_keygen(pctx, &pkey);

seems to work but i have no idea how to export the public key in uncompressed bin format. or how to import the other sides public key.

any help?

Anton Vainer
  • 97
  • 1
  • 10

1 Answers1

4

Importing the other side's public key from raw binary format can be done with the EVP_PKEY_new_raw_public_key() function. Man page here:

https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_new_raw_public_key.html

Exporting the public key in raw binary format is a little more tricky since there is no function to do it. You can do it in SubjectPublicKeyInfo format using i2d_PUBKEY() described here:

https://www.openssl.org/docs/man1.1.1/man3/i2d_PUBKEY.html

Fortunately the SubjectPublicKeyInfo format has the raw public key as the last 32 bytes of its output. So you can use i2d_PUBKEY() and just use the last the 32 bytes.

Matt Caswell
  • 8,167
  • 25
  • 28
  • I created an OpenSSL issue for the lack of function to export the raw key in binary format: https://github.com/openssl/openssl/issues/6259 – Matt Caswell May 15 '18 at 14:24
  • EVP_PKEY_new_raw_public_key seems to create a new key, but how does that work with ECDH? i need to get it from the context or the EVP_PKEY i already created? and it still returns EVP_PKEY and not the row buffer – Anton Vainer May 15 '18 at 14:54
  • You need two EVP_PKEY objects. One containing your private/public key pair (i.e. the one you generated in the EVP_PKEY_keygen() call in your question), and one containing the public key of the peer (e.g. created using EVP_PKEY_new_raw_public_key()). To generate the X25519 shared secret you then call EVP_PKEY_derive(). See the example on the man page: https://www.openssl.org/docs/man1.1.1/man3/EVP_PKEY_derive.html – Matt Caswell May 15 '18 at 20:39
  • i2d_PUBKEY filled an array with: first 4: 0x00 0x53 0xb5 0x01 all the rest are 0x00 but PEM_write_PUBKEY printed: MCowBQYDK2VuAyEADzQvR1de+xLCZrth9eRv62fXAaJooth0xJj1uVE4y14= – Anton Vainer May 16 '18 at 13:21
  • Ah, I suspect you ran into a particular quirk of the OpenSSL API, namely that all the "i2d" functions increment the value of the supplied buffer to point to *after* the end of the DER encoded data. So if you immediately try to look at the data in the buffer without decrementing it again (or saving a pointer to the location of the original buffer) you just get rubbish back. See the description of i2d_TYPE() here: https://www.openssl.org/docs/man1.1.1/man3/d2i_X509.html – Matt Caswell May 16 '18 at 14:25
  • i saved passed a copy of the pointer to the i2d function and read the original pointer, same result as before. i checked the pointers before and after i2d and they are the same – Anton Vainer May 17 '18 at 12:29
  • What does the i2d function call return? If it is successful it should return a positive value that is the length of the encoded output. Can you post the code somewhere? – Matt Caswell May 17 '18 at 12:51
  • `EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); EVP_PKEY_keygen_init(pctx); EVP_PKEY_keygen(pctx, &pkey); userbin = (unsigned char **)&userSharedBin; printf("&userSharedBin=%p userbin=%p", &userSharedBin, userbin); userSharedBinSize = i2d_PUBKEY(openSslKey, userbin); printf("&userSharedBin=%p userbin=%p", &userSharedBin, userbin); LOG_INFO(threadIndex, "size is %d", userSharedBinSize); ` the last print is 44 – Anton Vainer May 21 '18 at 06:17
  • Why do you pass `openSslKey` in your call to `i2d_PUBKEY` and not `pkey`? – Matt Caswell May 21 '18 at 07:10
  • its from different functions, it is the same parameter, sorry for the confusion – Anton Vainer May 21 '18 at 11:53
  • `EVP_PKEY_CTX *pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_X25519, NULL); EVP_PKEY *pkey = NULL; unsigned char userSharedBin[100]; unsigned char *userbin; int userSharedBinSize; EVP_PKEY_keygen_init(pctx); EVP_PKEY_keygen(pctx, &pkey); userbin = userSharedBin; printf("&userSharedBin=%p userbin=%p\n", &userSharedBin, userbin); userSharedBinSize = i2d_PUBKEY(pkey, &userbin); printf("&userSharedBin=%p userbin=%p\n", &userSharedBin, userbin); printf("size is %d\n", userSharedBinSize); BIO_dump_fp(stdout, userSharedBin, userSharedBinSize);` – Matt Caswell May 21 '18 at 15:17
  • I made some tweaks to your code. The above now works for me as expected. – Matt Caswell May 21 '18 at 15:18
  • i got the other side ECP_PKEY, how do i compute the shared secret? – Anton Vainer May 28 '18 at 09:52
  • i'm having trouble importing i2d output to mbedTLS format and i see that its not the expected format. what is the 'row' format? – Anton Vainer May 28 '18 at 13:32
  • Use the `EVP_PKEY_derive` function to derive a shared secret: https://www.openssl.org/docs/man1.1.0/crypto/EVP_PKEY_derive.html – Matt Caswell May 28 '18 at 21:40
  • after derive, do i get the actual X point or i get it hashed (for use as a key)? – Anton Vainer May 29 '18 at 13:35
  • You have to hash it yourself. – Matt Caswell May 29 '18 at 19:30