Last active
August 29, 2017 18:48
-
-
Save melvyniandrag/c8bfb6ea6f10f648381ccedcc1ac0912 to your computer and use it in GitHub Desktop.
Creating and sharing ECDH keys to create a shared secret with openssl.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Compile me with g++ main.cpp -lssl -lcrypto | |
The computeNeededSharedMemory() function has alot of stuff that is duplicated in main(). It should be refactored. | |
Also I forgot to free some stuff at the end. Just add the appropriate calls to the appropriate free methods. For me, its lunchtime | |
now. | |
*/ | |
#include <cassert> | |
#include <string> | |
#include <cstring> | |
#include <iostream> | |
#include <openssl/crypto.h> | |
#include <openssl/evp.h> | |
#include <openssl/ec.h> | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <string.h> | |
#include <sys/mman.h> | |
#include <unistd.h> | |
size_t computeNeededSharedMem() | |
{ | |
// If I don't set these to NULL, we get a segfault. | |
EVP_PKEY_CTX *pctx = NULL; | |
EVP_PKEY_CTX *kctx = NULL; | |
EVP_PKEY_CTX *ctx = NULL; | |
EVP_PKEY *pkey = NULL; | |
EVP_PKEY *peerkey = NULL; | |
EVP_PKEY *params = NULL; | |
assert(NULL != (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))); | |
assert(1 == EVP_PKEY_paramgen_init(pctx)); | |
assert(1 == EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)); | |
assert(1 == EVP_PKEY_paramgen(pctx, ¶ms)); | |
assert(NULL != (kctx = EVP_PKEY_CTX_new(params, NULL))); | |
assert(1 == EVP_PKEY_keygen_init(kctx)); | |
assert(1 == EVP_PKEY_keygen(kctx, &pkey)); | |
assert(1 == EVP_PKEY_keygen(kctx, &peerkey)); // Just populating the key with some stuff. Commenting this out causes a segfault. | |
assert( 0 == EVP_PKEY_cmp( pkey, peerkey ) ); | |
// Verify the keys can be serialized / deserialized correctly. | |
// Serialize.. | |
EC_KEY *Key1 = EVP_PKEY_get1_EC_KEY( pkey ); | |
assert( NULL != Key1 ); | |
const EC_POINT *Point1 = EC_KEY_get0_public_key( Key1 ); | |
assert( NULL != Point1 ); | |
const EC_GROUP *gp = EC_KEY_get0_group( Key1 ); | |
assert( gp != NULL ); | |
const size_t SizeOfBuffer = EC_POINT_point2oct(gp, Point1, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); | |
unsigned char * serializedKey = static_cast<unsigned char*>( OPENSSL_malloc(SizeOfBuffer) ); | |
const size_t VerifyBufferSize = EC_POINT_point2oct(gp, Point1, POINT_CONVERSION_UNCOMPRESSED, serializedKey, SizeOfBuffer, NULL); | |
assert( SizeOfBuffer == VerifyBufferSize ); | |
return SizeOfBuffer; | |
} | |
void* create_shared_memory(size_t size) { | |
int protection = PROT_READ | PROT_WRITE; | |
int visibility = MAP_ANONYMOUS | MAP_SHARED; | |
return mmap(NULL, size, protection, visibility, 0, 0); | |
} | |
int main(int argc, char** argv){ | |
size_t SizeOfSharedBuffer = computeNeededSharedMem(); | |
void *shmem = create_shared_memory( SizeOfSharedBuffer ); | |
int pid = fork(); | |
// If I don't set these to NULL, we get a segfault. | |
EVP_PKEY_CTX *pctx = NULL; | |
EVP_PKEY_CTX *kctx = NULL; | |
EVP_PKEY_CTX *ctx = NULL; | |
EVP_PKEY *pkey = NULL; | |
EVP_PKEY *peerkey = NULL; | |
EVP_PKEY *params = NULL; | |
assert(NULL != (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))); | |
assert(1 == EVP_PKEY_paramgen_init(pctx)); | |
assert(1 == EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)); | |
assert(1 == EVP_PKEY_paramgen(pctx, ¶ms)); | |
assert(NULL != (kctx = EVP_PKEY_CTX_new(params, NULL))); | |
assert(1 == EVP_PKEY_keygen_init(kctx)); | |
assert(1 == EVP_PKEY_keygen(kctx, &pkey)); | |
assert(1 == EVP_PKEY_keygen(kctx, &peerkey)); // Just populating the key with some stuff. Commenting this out causes a segfault. | |
assert( 0 == EVP_PKEY_cmp( pkey, peerkey ) ); | |
// Verify the keys can be serialized / deserialized correctly. | |
// Serialize.. | |
EC_KEY *Key1 = EVP_PKEY_get1_EC_KEY( pkey ); | |
assert( NULL != Key1 ); | |
const EC_POINT *Point1 = EC_KEY_get0_public_key( Key1 ); | |
assert( NULL != Point1 ); | |
const EC_GROUP *gp = EC_KEY_get0_group( Key1 ); | |
assert( gp != NULL ); | |
const size_t SizeOfBuffer = EC_POINT_point2oct(gp, Point1, POINT_CONVERSION_UNCOMPRESSED, NULL, 0, NULL); | |
unsigned char * serializedKey = static_cast<unsigned char*>( OPENSSL_malloc(SizeOfBuffer) ); | |
const size_t VerifyBufferSize = EC_POINT_point2oct(gp, Point1, POINT_CONVERSION_UNCOMPRESSED, serializedKey, SizeOfBuffer, NULL); | |
assert( SizeOfBuffer == VerifyBufferSize ); | |
assert( serializedKey != NULL ); | |
// Exchange keys. | |
unsigned char * recvSerializedKey = static_cast<unsigned char*>( OPENSSL_malloc(SizeOfBuffer) ); | |
if (pid == 0) | |
{ | |
memcpy( shmem, serializedKey, SizeOfBuffer ); | |
std::cout << "Parent Sent\n:" << std::string( reinterpret_cast<char *>( serializedKey ) ) << "\n"; | |
sleep(1); | |
} | |
else | |
{ | |
sleep(1); | |
memcpy( recvSerializedKey, shmem, SizeOfBuffer ); | |
std::cout << "Child Received\n:" << std::string( reinterpret_cast<char *>( recvSerializedKey ) ) << "\n"; | |
} | |
sleep(1); // Just wait another second to make sure we're synced up. | |
if (pid != 0) | |
{ | |
memcpy( shmem, serializedKey, SizeOfBuffer ); | |
std::cout << "Child Sent:\n" << std::string( reinterpret_cast<char *>( serializedKey ) ) << "\n"; | |
sleep(1); | |
} | |
else | |
{ | |
sleep(1); | |
memcpy( recvSerializedKey, shmem, SizeOfBuffer ); | |
std::cout << "Parent Received:\n" << std::string( reinterpret_cast< char *>( recvSerializedKey) ) << "\n"; | |
} | |
// Deserialize | |
EC_POINT *deserialPt = EC_POINT_new( gp ); | |
assert( 1 == EC_POINT_oct2point( gp, deserialPt, recvSerializedKey, SizeOfBuffer, NULL ) ); | |
EC_KEY *Key2 = EVP_PKEY_get1_EC_KEY( peerkey ); | |
assert( 1 == EC_KEY_set_public_key( Key2, deserialPt ) ); | |
assert( Key2 != NULL ); | |
assert( 1 == EVP_PKEY_set1_EC_KEY( peerkey, Key2 ) ); | |
// Done verifying. | |
assert( 0 == EVP_PKEY_cmp( pkey, peerkey ) ); // The two parties ought to have different keys! | |
// Generate the shared secret: | |
size_t secret_len; | |
unsigned char* secret; | |
assert( NULL != (ctx = EVP_PKEY_CTX_new( pkey, NULL ) ) ); | |
assert( 1 == EVP_PKEY_derive_init(ctx) ); | |
assert( 1 == EVP_PKEY_derive_set_peer(ctx, peerkey)); | |
assert( 1 == EVP_PKEY_derive(ctx, NULL, &secret_len)); | |
assert( NULL != (secret = static_cast<unsigned char*>( OPENSSL_malloc( secret_len ) ) ) ); | |
assert( 1 == ( EVP_PKEY_derive( ctx, secret, &secret_len ) ) ); | |
std::cout << "Shared secret: " << std::string( reinterpret_cast<char *>( secret ) ) << std::endl; | |
EVP_PKEY_CTX_free(ctx); | |
EVP_PKEY_free(peerkey); | |
EVP_PKEY_free(pkey); | |
EVP_PKEY_CTX_free(kctx); | |
EVP_PKEY_free(params); | |
EVP_PKEY_CTX_free(pctx); | |
EC_KEY_free( Key1 ); | |
// EC_POINT_free( Point1 ); // Can't free the Point because its a const pointer. It wont let me free() even if I use const_cast. | |
// EC_POINT_free( const_cast< EC_POINT *>( Point1 ) ); // Can't free the Point because its a const pointer. It wont let me free() even if I use const_cast. | |
EC_KEY_free( Key2 ); | |
std::cout << "Successful run!" << std::endl; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment