5

I need to generate an EC Diffie Hellman key pair. I am using the secp256r1 named curve, and OpenSSL. This is what I have with me so far:

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))) 
        printf("Error in EC key generation\n");

    /* Initialise the parameter generation */
    if(1 != EVP_PKEY_paramgen_init(pctx)) 
        printf("Error in EC key generation\n");

    /* 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)) 
        printf("Error in EC key generation\n");

    /* Create the parameter object params */
    if (!EVP_PKEY_paramgen(pctx, &params)) 
        printf("Error in EC key generation\n");

    /* Create the context for the key generation */
    if(NULL == (kctx = EVP_PKEY_CTX_new(params, NULL))) 
        printf("Error in EC key generation\n");

    /* Generate the key */
    if(1 != EVP_PKEY_keygen_init(kctx)) 
        printf("Error in EC key generation\n");

    if (1 != EVP_PKEY_keygen(kctx, &pkey)) 
        printf("Error in EC key generation\n");

    /* 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))) 
        printf("Error in EC key generation\n");

    /* Initialise */
    if(1 != EVP_PKEY_derive_init(ctx)) 
        printf("Error in EC key generation\n");

    /* Provide the peer public key */
    if(1 != EVP_PKEY_derive_set_peer(ctx, peerkey)) 
        printf("Error in EC key generation\n");

    /* Determine buffer length for shared secret */
    if(1 != EVP_PKEY_derive(ctx, NULL, secret_len)) 
        printf("Error in EC key generation\n");

    /* Create the buffer */
    if(NULL == (secret = OPENSSL_malloc(*secret_len))) 
        printf("Error in EC key generation\n");

    /* Derive the shared secret */
    if(1 != (EVP_PKEY_derive(ctx, secret, secret_len))) 
        printf("Error in EC key generation\n");

    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;
} 

I figured out that for this function to work, i need a EVP_KEY object with the second party's public key. I have this public key in a byte array, and also its length. How do I convert it to the required type? And also I could not find the secp256r1 curve in OpenSSL, but I used the one in the code after doing some research. Is it correct?

Thanks !

previouslyactualname
  • 703
  • 2
  • 11
  • 25
  • I'm not an expert on OpenSSL, but the one thing I'd warn you about is implementing your own cryptography can be dangerous. For example, you shouldn't just take whatever key you receive and trust that it belongs to the party you are intending to communicate with: that would make you subject to [man in the middle attack](http://en.wikipedia.org/wiki/Man-in-the-middle_attack). So there needs to be some type of authentication that the key belongs to the person who claims it. – TheGreatContini Jul 23 '14 at 23:56
  • Yes actually now I found out that the key comes with a signature, (the server key exchange record in TLS is what I'm talking about), so the security issue is good I think :) I can't extract the public key from the signature though – previouslyactualname Jul 24 '14 at 13:12
  • What does your public key look like? Does this answer apply? http://stackoverflow.com/questions/2918923/evp-pkey-from-char-buffer-in-x509-pkcs7 – Chiara Hsieh Jul 25 '14 at 02:41

2 Answers2

1

The peer's public key is a point on the curve. From crypto\ec\ec_lcl.h:

struct ec_key_st {
    int version;

    EC_GROUP *group;

    EC_POINT *pub_key;
    BIGNUM   *priv_key;

    unsigned int enc_flag;
    point_conversion_form_t conv_form;

    int     references;
    int flags;

    EC_EXTRA_DATA *method_data;
} /* EC_KEY */;

I believe you need to call EC_POINT_new (c_lcl.h is a private header, so you won't have access to the structure).

Luckily, there's a lot of functions to manipulate them. From the EC_POINT_new(3) docs:

EC_POINTs can be converted to and from various external representations. Supported representations are octet strings, BIGNUMs and hexadecimal. The format of the external representation is described by the point_conversion_form. See EC_GROUP_copy(3) for a description of point_conversion_form. Octet strings are stored in a buffer along with an associated buffer length. A point held in a BIGNUM is calculated by converting the point to an octet string and then converting that octet string into a BIGNUM integer. Points in hexadecimal format are stored in a NULL terminated character string where each character is one of the printable values 0-9 or A-F (or a-f).

Also see EC_POINT_set_affine_coordinates_GFp, EC_POINT_set_affine_coordinates_GF2m and EC_KEY_set_public_key:

$ grep -R EC_KEY_set_public_key *
crypto/ec/ec.h:int EC_KEY_set_public_key(EC_KEY *key, const EC_POINT *pub);
...

You can see an example of how to set the point on the OpenSSL wiki at Elliptic Curve Cryptography.

jww
  • 97,681
  • 90
  • 411
  • 885
1

I understand this is an about 8 years old question but with OpenSSL 3 release last autumn my answer may be useful for someone who just started to use Elliptic-curve Diffie–Hellman key exchange with OpenSSL 3.

The original title and body questions are different. Basically, there are 3 questions:

  • How to generate ECDH keys?
  • How to serialize/deserialize the peer public key?
  • What EC named curve to use?

ECDH KEY GENERATION (OpenSSL 3, plain C)

const char curveName[] = "...";

OSSL_PARAM_BLD* paramBuild = OSSL_PARAM_BLD_new();
if (paramBuild == NULL) {
  // Failed.
  return;
}

// Push the curve name to the OSSL_PARAM_BLD.
if (OSSL_PARAM_BLD_push_utf8_string(paramBuild,
    OSSL_PKEY_PARAM_GROUP_NAME, curveName, 0) != 1) {
  // Failed.
  OSSL_PARAM_BLD_free(paramBuild);
  return;
}

// Convert OSSL_PARAM_BLD to OSSL_PARAM.
OSSL_PARAM* params = OSSL_PARAM_BLD_to_param(paramBuild);
if (params == NULL) {
  // Failed.
  OSSL_PARAM_BLD_free(paramBuild);
  return;
}

// Create the EC key generation context.
EVP_PKEY_CTX* ctx = EVP_PKEY_CTX_new_from_name(NULL, "EC", NULL);
if (ctx == NULL) {
  // Failed.
  OSSL_PARAM_free(params);
  OSSL_PARAM_BLD_free(paramBuild);
  return;
}

// Initialize the key generation context.
if (EVP_PKEY_keygen_init(ctx) <= 0) {
  // Failed.
  EVP_PKEY_CTX_free(ctx);
  OSSL_PARAM_free(params);
  OSSL_PARAM_BLD_free(paramBuild);
  return;
}

// Set the parameters which include the curve name.
if (EVP_PKEY_CTX_set_params(ctx, params) != 1) {
  EVP_PKEY_CTX_free(ctx);
  OSSL_PARAM_free(params);
  OSSL_PARAM_BLD_free(paramBuild);
  return;
}

// Generate a key pair.
EVP_PKEY* keyPair = NULL;
if (EVP_PKEY_generate(ctx, &keyPair) <= 0) {
  EVP_PKEY_CTX_free(ctx);
  OSSL_PARAM_free(params);
  OSSL_PARAM_BLD_free(paramBuild);
  return;
}

// Free auxiliary things...
EVP_PKEY_CTX_free(ctx);
OSSL_PARAM_free(params);
OSSL_PARAM_BLD_free(paramBuild);

// Now keyPair has the generated private and public keys.
// Use EVP_PKEY_free(keyPair) when you finish using them.

See a full C++ OpenSSL 3 example: ec-diffie-hellman-openssl.h, ec-diffie-hellman-openssl.cpp.

PUBLIC KEY SERIALIZATION/DESERIALIZATION

Now, you have to serialize Alice and Bob public keys, transfer them to the other sides and use them for the shared secret derivation. See my answer here: How does one access the raw ECDH public key, private key and params inside OpenSSL's EVP_PKEY structure?

ECDH NAMED CURVES

I would recommend to follow FIPS 140-3 even if you are not required to, if it does not introduce performance issues (or maybe significant performance issues) for your specific case. For example, Implementation Guidance for FIPS 140-3 and the Cryptographic Module Validation Program mentions NIST SP 800-56Arev3:

Question/Problem Are there allowed elliptic curves for use in the ECDSA signature algorithm and the ECC-based key agreement schemes in an approved mode of operation? If so, what are the requirements for their use?

Resolution Elliptic curves approved for use in ECDSA are specified in SP 800-186, as implemented in FIPS 186-5. Elliptic curves approved for use in ECC-based key agreement schemes are specified in Appendix D of SP 800- 56Arev3.

See Appendix D: Approved ECC Curves and FFC Safe-prime Groups of NIST Special Publication 800-56A Revision 3 for approved ECC curves.

Use EC_get_builtin_curves to enumerated OpenSSL supported curves. See the ECDiffieHellmanOpenSSL::GetSupportedCurves method here.

Eugen
  • 479
  • 5
  • 16