Elliptic Curve Diffie Hellman
Elliptic Curve Diffie Hellman (ECDH) is an Elliptic Curve variant of the standard Diffie Hellman algorithm. See Elliptic Curve Cryptography for an overview of the basic concepts behind Elliptic Curve algorithms.
ECDH is used for the purposes of key agreement. Suppose two people, Alice and Bob, which to exchange a secret key with each other. Alice will generate a private key dA and a public key QA=dAG (where G is the generator for the curve). Similarly Bob has his private key dB and a public key QB=dBG. If Bob sends his public key to Alice then she can calculate dAQB=dAdBG. Similarly if Alice sends her public key to Bob, then he can calculate dbQA=dAdBG. The shared secret is the x co-ordinate of the calculated point dAdBG. Any eavesdropper would only know QA and QB, and would be unable to calculate the shared secret.
Using ECDH in OpenSSL
In order for two peers to exchange a shared secret they need to first agree on the parameters to be used. In Elliptic Curve Cryptography this is typically done through the use of named curves. A named curve is simply a well defined and well known set of parameters that define an elliptic curve. OpenSSL has support for a wide variety of different well known named curves. In the example below the ANSI X9.62 Prime 256v1 curve is used.
The example below shows how to set up the parameters based on the use of a named curve, how to generate a public/private key pair for those parameters and subsequently how to derive a shared secret. The details of how to obtain the other party's key (the peer key) are omitted, as this is specific to your particular situation. Note that you do not necessarily need to generate a new private/public key pair for every exchange (although you may choose to do so). Also note that the derived shared secret is not suitable for use directly as a shared key. Typically the shared secret is passed through some hash function first in order to generate a key.
See below for the example code.
unsigned char *ecdh(size_t *secret_len) { EVP_PKEY_CTX *pctx, *kctx; EVP_PKEY_CTX *ctx; unsigned char *secret; EVP_PKEY *pkey = NULL, *peerkey, *params = NULL; /* NB: assumes pkey, peerkey have been already set up */ /* Create the context for parameter generation */ if(NULL == (pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL))) handleErrors(); /* Initialise the parameter generation */ if(1 != EVP_PKEY_paramgen_init(pctx)) handleErrors(); /* We're going to use the ANSI X9.62 Prime 256v1 curve */ if(1 != EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, NID_X9_62_prime256v1)) handleErrors(); /* Create the parameter object params */ if (!EVP_PKEY_paramgen(pctx, ¶ms)) handleErrors(); /* Create the context for the key generation */ if(NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) handleErrors(); /* Generate the key */ if(1 != EVP_PKEY_keygen_init(kctx)) handleErrors(); if (1 != EVP_PKEY_keygen(kctx, &pkey)) handleErrors(); /* Get the peer's public key, and provide the peer with our public key - * how this is done will be specific to your circumstances */ peerkey = get_peerkey(pkey); /* Create the context for the shared secret derivation */ if(NULL == (ctx = EVP_PKEY_CTX_new(pkey, NULL))) handleErrors(); /* Initialise */ if(1 != EVP_PKEY_derive_init(ctx)) handleErrors(); /* Provide the peer public key */ if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) handleErrors(); /* Determine buffer length for shared secret */ if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) handleErrors(); /* Create the buffer */ if(NULL == (secret = OPENSSL_malloc(*secret_len))) handleErrors(); /* Dervive the shared secret */ if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) handleErrors(); 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); /* Never use a derived secret directly. Typically it is passed * through some hash function to produce a key */ return secret; }
You should also refer to the EVP Key Derivation page for general information on the key agreement API in OpenSSL.