1 | /*
|
---|
2 | * Copyright 2023-2024 The OpenSSL Project Authors. All Rights Reserved.
|
---|
3 | *
|
---|
4 | * Licensed under the Apache License 2.0 (the "License"). You may not use
|
---|
5 | * this file except in compliance with the License. You can obtain a copy
|
---|
6 | * in the file LICENSE in the source distribution or at
|
---|
7 | * https://www.openssl.org/source/license.html
|
---|
8 | */
|
---|
9 |
|
---|
10 | #include <stdio.h>
|
---|
11 | #include <string.h>
|
---|
12 | #include <openssl/core_names.h>
|
---|
13 | #include <openssl/evp.h>
|
---|
14 | #include <openssl/err.h>
|
---|
15 |
|
---|
16 | /*
|
---|
17 | * This is a demonstration of key exchange using ECDH.
|
---|
18 | *
|
---|
19 | * EC key exchange requires 2 parties (peers) to first agree on shared group
|
---|
20 | * parameters (the EC curve name). Each peer then generates a public/private
|
---|
21 | * key pair using the shared curve name. Each peer then gives their public key
|
---|
22 | * to the other peer. A peer can then derive the same shared secret using their
|
---|
23 | * private key and the other peers public key.
|
---|
24 | */
|
---|
25 |
|
---|
26 | /* Object used to store information for a single Peer */
|
---|
27 | typedef struct peer_data_st {
|
---|
28 | const char *name; /* name of peer */
|
---|
29 | const char *curvename; /* The shared curve name */
|
---|
30 | EVP_PKEY *priv; /* private keypair */
|
---|
31 | EVP_PKEY *pub; /* public key to send to other peer */
|
---|
32 | unsigned char *secret; /* allocated shared secret buffer */
|
---|
33 | size_t secretlen;
|
---|
34 | } PEER_DATA;
|
---|
35 |
|
---|
36 | /*
|
---|
37 | * The public key needs to be given to the other peer
|
---|
38 | * The following code extracts the public key data from the private key
|
---|
39 | * and then builds an EVP_KEY public key.
|
---|
40 | */
|
---|
41 | static int get_peer_public_key(PEER_DATA *peer, OSSL_LIB_CTX *libctx)
|
---|
42 | {
|
---|
43 | int ret = 0;
|
---|
44 | EVP_PKEY_CTX *ctx;
|
---|
45 | OSSL_PARAM params[3];
|
---|
46 | unsigned char pubkeydata[256];
|
---|
47 | size_t pubkeylen;
|
---|
48 |
|
---|
49 | /* Get the EC encoded public key data from the peers private key */
|
---|
50 | if (!EVP_PKEY_get_octet_string_param(peer->priv, OSSL_PKEY_PARAM_PUB_KEY,
|
---|
51 | pubkeydata, sizeof(pubkeydata),
|
---|
52 | &pubkeylen))
|
---|
53 | return 0;
|
---|
54 |
|
---|
55 | /* Create a EC public key from the public key data */
|
---|
56 | ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL);
|
---|
57 | if (ctx == NULL)
|
---|
58 | return 0;
|
---|
59 | params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
|
---|
60 | (char *)peer->curvename, 0);
|
---|
61 | params[1] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,
|
---|
62 | pubkeydata, pubkeylen);
|
---|
63 | params[2] = OSSL_PARAM_construct_end();
|
---|
64 | ret = EVP_PKEY_fromdata_init(ctx) > 0
|
---|
65 | && (EVP_PKEY_fromdata(ctx, &peer->pub, EVP_PKEY_PUBLIC_KEY,
|
---|
66 | params) > 0);
|
---|
67 | EVP_PKEY_CTX_free(ctx);
|
---|
68 | return ret;
|
---|
69 | }
|
---|
70 |
|
---|
71 | static int create_peer(PEER_DATA *peer, OSSL_LIB_CTX *libctx)
|
---|
72 | {
|
---|
73 | int ret = 0;
|
---|
74 | EVP_PKEY_CTX *ctx = NULL;
|
---|
75 | OSSL_PARAM params[2];
|
---|
76 |
|
---|
77 | params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
|
---|
78 | (char *)peer->curvename, 0);
|
---|
79 | params[1] = OSSL_PARAM_construct_end();
|
---|
80 |
|
---|
81 | ctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", NULL);
|
---|
82 | if (ctx == NULL)
|
---|
83 | return 0;
|
---|
84 |
|
---|
85 | if (EVP_PKEY_keygen_init(ctx) <= 0
|
---|
86 | || !EVP_PKEY_CTX_set_params(ctx, params)
|
---|
87 | || EVP_PKEY_generate(ctx, &peer->priv) <= 0
|
---|
88 | || !get_peer_public_key(peer, libctx)) {
|
---|
89 | EVP_PKEY_free(peer->priv);
|
---|
90 | peer->priv = NULL;
|
---|
91 | goto err;
|
---|
92 | }
|
---|
93 | ret = 1;
|
---|
94 | err:
|
---|
95 | EVP_PKEY_CTX_free(ctx);
|
---|
96 | return ret;
|
---|
97 | }
|
---|
98 |
|
---|
99 | static void destroy_peer(PEER_DATA *peer)
|
---|
100 | {
|
---|
101 | EVP_PKEY_free(peer->priv);
|
---|
102 | EVP_PKEY_free(peer->pub);
|
---|
103 | }
|
---|
104 |
|
---|
105 | static int generate_secret(PEER_DATA *peerA, EVP_PKEY *peerBpub,
|
---|
106 | OSSL_LIB_CTX *libctx)
|
---|
107 | {
|
---|
108 | unsigned char *secret = NULL;
|
---|
109 | size_t secretlen = 0;
|
---|
110 | EVP_PKEY_CTX *derivectx;
|
---|
111 |
|
---|
112 | /* Create an EVP_PKEY_CTX that contains peerA's private key */
|
---|
113 | derivectx = EVP_PKEY_CTX_new_from_pkey(libctx, peerA->priv, NULL);
|
---|
114 | if (derivectx == NULL)
|
---|
115 | return 0;
|
---|
116 |
|
---|
117 | if (EVP_PKEY_derive_init(derivectx) <= 0)
|
---|
118 | goto cleanup;
|
---|
119 | /* Set up peerB's public key */
|
---|
120 | if (EVP_PKEY_derive_set_peer(derivectx, peerBpub) <= 0)
|
---|
121 | goto cleanup;
|
---|
122 |
|
---|
123 | /*
|
---|
124 | * For backwards compatibility purposes the OpenSSL ECDH provider supports
|
---|
125 | * optionally using a X963KDF to expand the secret data. This can be done
|
---|
126 | * with code similar to the following.
|
---|
127 | *
|
---|
128 | * OSSL_PARAM params[5];
|
---|
129 | * size_t outlen = 128;
|
---|
130 | * unsigned char ukm[] = { 1, 2, 3, 4 };
|
---|
131 | * params[0] = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_TYPE,
|
---|
132 | * "X963KDF", 0);
|
---|
133 | * params[1] = OSSL_PARAM_construct_utf8_string(OSSL_EXCHANGE_PARAM_KDF_DIGEST,
|
---|
134 | * "SHA256", 0);
|
---|
135 | * params[2] = OSSL_PARAM_construct_size_t(OSSL_EXCHANGE_PARAM_KDF_OUTLEN,
|
---|
136 | * &outlen);
|
---|
137 | * params[3] = OSSL_PARAM_construct_octet_string(OSSL_EXCHANGE_PARAM_KDF_UKM,
|
---|
138 | * ukm, sizeof(ukm));
|
---|
139 | * params[4] = OSSL_PARAM_construct_end();
|
---|
140 | * if (!EVP_PKEY_CTX_set_params(derivectx, params))
|
---|
141 | * goto cleanup;
|
---|
142 | *
|
---|
143 | * Note: After the secret is generated below, the peer could alternatively
|
---|
144 | * pass the secret to a KDF to derive additional key data from the secret.
|
---|
145 | * See demos/kdf/hkdf.c for an example (where ikm is the secret key)
|
---|
146 | */
|
---|
147 |
|
---|
148 | /* Calculate the size of the secret and allocate space */
|
---|
149 | if (EVP_PKEY_derive(derivectx, NULL, &secretlen) <= 0)
|
---|
150 | goto cleanup;
|
---|
151 | secret = (unsigned char *)OPENSSL_malloc(secretlen);
|
---|
152 | if (secret == NULL)
|
---|
153 | goto cleanup;
|
---|
154 |
|
---|
155 | /*
|
---|
156 | * Derive the shared secret. In this example 32 bytes are generated.
|
---|
157 | * For EC curves the secret size is related to the degree of the curve
|
---|
158 | * which is 256 bits for P-256.
|
---|
159 | */
|
---|
160 | if (EVP_PKEY_derive(derivectx, secret, &secretlen) <= 0)
|
---|
161 | goto cleanup;
|
---|
162 | peerA->secret = secret;
|
---|
163 | peerA->secretlen = secretlen;
|
---|
164 |
|
---|
165 | printf("Shared secret (%s):\n", peerA->name);
|
---|
166 | BIO_dump_indent_fp(stdout, peerA->secret, peerA->secretlen, 2);
|
---|
167 | putchar('\n');
|
---|
168 |
|
---|
169 | return 1;
|
---|
170 | cleanup:
|
---|
171 | OPENSSL_free(secret);
|
---|
172 | EVP_PKEY_CTX_free(derivectx);
|
---|
173 | return 0;
|
---|
174 | }
|
---|
175 |
|
---|
176 | int main(void)
|
---|
177 | {
|
---|
178 | int ret = EXIT_FAILURE;
|
---|
179 | /* Initialise the 2 peers that will share a secret */
|
---|
180 | PEER_DATA peer1 = {"peer 1", "P-256"};
|
---|
181 | PEER_DATA peer2 = {"peer 2", "P-256"};
|
---|
182 | /*
|
---|
183 | * Setting libctx to NULL uses the default library context
|
---|
184 | * Use OSSL_LIB_CTX_new() to create a non default library context
|
---|
185 | */
|
---|
186 | OSSL_LIB_CTX *libctx = NULL;
|
---|
187 |
|
---|
188 | /* Each peer creates a (Ephemeral) keypair */
|
---|
189 | if (!create_peer(&peer1, libctx)
|
---|
190 | || !create_peer(&peer2, libctx)) {
|
---|
191 | fprintf(stderr, "Create peer failed\n");
|
---|
192 | goto cleanup;
|
---|
193 | }
|
---|
194 |
|
---|
195 | /*
|
---|
196 | * Each peer uses its private key and the other peers public key to
|
---|
197 | * derive a shared secret
|
---|
198 | */
|
---|
199 | if (!generate_secret(&peer1, peer2.pub, libctx)
|
---|
200 | || !generate_secret(&peer2, peer1.pub, libctx)) {
|
---|
201 | fprintf(stderr, "Generate secrets failed\n");
|
---|
202 | goto cleanup;
|
---|
203 | }
|
---|
204 |
|
---|
205 | /* For illustrative purposes demonstrate that the derived secrets are equal */
|
---|
206 | if (peer1.secretlen != peer2.secretlen
|
---|
207 | || CRYPTO_memcmp(peer1.secret, peer2.secret, peer1.secretlen) != 0) {
|
---|
208 | fprintf(stderr, "Derived secrets do not match\n");
|
---|
209 | goto cleanup;
|
---|
210 | } else {
|
---|
211 | fprintf(stdout, "Derived secrets match\n");
|
---|
212 | }
|
---|
213 |
|
---|
214 | ret = EXIT_SUCCESS;
|
---|
215 | cleanup:
|
---|
216 | if (ret != EXIT_SUCCESS)
|
---|
217 | ERR_print_errors_fp(stderr);
|
---|
218 | destroy_peer(&peer2);
|
---|
219 | destroy_peer(&peer1);
|
---|
220 | return ret;
|
---|
221 | }
|
---|