#include <stdio.h>
#include <string.h>
#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/err.h>
typedef struct peer_data_st {
const char *name;
const char *curvename;
EVP_PKEY *priv;
EVP_PKEY *pub;
unsigned char *secret;
size_t secretlen;
} PEER_DATA;
static int get_peer_public_key(PEER_DATA *peer, OSSL_LIB_CTX *libctx)
{
int ret = 0;
EVP_PKEY_CTX *ctx;
OSSL_PARAM params[3];
unsigned char pubkeydata[256];
size_t pubkeylen;
if (!EVP_PKEY_get_octet_string_param(peer->priv, OSSL_PKEY_PARAM_PUB_KEY,
pubkeydata, sizeof(pubkeydata),
&pubkeylen))
return 0;
ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL);
if (ctx == NULL)
return 0;
params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
(char *)peer->curvename, 0);
params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
pubkeydata, pubkeylen);
params[2] = OSSL_PARAM_construct_end();
ret = EVP_PKEY_fromdata_init(ctx) > 0
&& (EVP_PKEY_fromdata(ctx, &peer->pub, EVP_PKEY_PUBLIC_KEY,
params)
> 0);
EVP_PKEY_CTX_free(ctx);
return ret;
}
static int create_peer(PEER_DATA *peer, OSSL_LIB_CTX *libctx)
{
int ret = 0;
EVP_PKEY_CTX *ctx = NULL;
OSSL_PARAM params[2];
params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
(char *)peer->curvename, 0);
params[1] = OSSL_PARAM_construct_end();
ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL);
if (ctx == NULL)
return 0;
if (EVP_PKEY_keygen_init(ctx) <= 0
|| !EVP_PKEY_CTX_set_params(ctx, params)
|| EVP_PKEY_generate(ctx, &peer->priv) <= 0
|| !get_peer_public_key(peer, libctx)) {
EVP_PKEY_free(peer->priv);
peer->priv = NULL;
goto err;
}
ret = 1;
err:
EVP_PKEY_CTX_free(ctx);
return ret;
}
static void destroy_peer(PEER_DATA *peer)
{
EVP_PKEY_free(peer->priv);
EVP_PKEY_free(peer->pub);
}
static int generate_secret(PEER_DATA *peerA, EVP_PKEY *peerBpub,
OSSL_LIB_CTX *libctx)
{
unsigned char *secret = NULL;
size_t secretlen = 0;
EVP_PKEY_CTX *derivectx;
derivectx = EVP_PKEY_CTX_new_from_pkey(libctx, peerA->priv, NULL);
if (derivectx == NULL)
return 0;
if (EVP_PKEY_derive_init(derivectx) <= 0)
goto cleanup;
if (EVP_PKEY_derive_set_peer(derivectx, peerBpub) <= 0)
goto cleanup;
if (EVP_PKEY_derive(derivectx, NULL, &secretlen) <= 0)
goto cleanup;
secret = (unsigned char *)OPENSSL_malloc(secretlen);
if (secret == NULL)
goto cleanup;
if (EVP_PKEY_derive(derivectx, secret, &secretlen) <= 0)
goto cleanup;
peerA->secret = secret;
peerA->secretlen = secretlen;
printf("Shared secret (%s):\n", peerA->name);
BIO_dump_indent_fp(stdout, peerA->secret, peerA->secretlen, 2);
putchar('\n');
return 1;
cleanup:
OPENSSL_free(secret);
EVP_PKEY_CTX_free(derivectx);
return 0;
}
int main(void)
{
int ret = EXIT_FAILURE;
PEER_DATA peer1 = { "peer 1", "P-256" };
PEER_DATA peer2 = { "peer 2", "P-256" };
OSSL_LIB_CTX *libctx = NULL;
if (!create_peer(&peer1, libctx)
|| !create_peer(&peer2, libctx)) {
fprintf(stderr, "Create peer failed\n");
goto cleanup;
}
if (!generate_secret(&peer1, peer2.pub, libctx)
|| !generate_secret(&peer2, peer1.pub, libctx)) {
fprintf(stderr, "Generate secrets failed\n");
goto cleanup;
}
if (peer1.secretlen != peer2.secretlen
|| CRYPTO_memcmp(peer1.secret, peer2.secret, peer1.secretlen) != 0) {
fprintf(stderr, "Derived secrets do not match\n");
goto cleanup;
} else {
fprintf(stdout, "Derived secrets match\n");
}
ret = EXIT_SUCCESS;
cleanup:
if (ret != EXIT_SUCCESS)
ERR_print_errors_fp(stderr);
destroy_peer(&peer2);
destroy_peer(&peer1);
return ret;
}