1 /*-
2  * Copyright 2021-2022 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 /*
11  * Example showing how to generate an EC key and extract values from the
12  * generated key.
13  */
14 
15 #include <string.h>
16 #include <stdio.h>
17 #include <openssl/err.h>
18 #include <openssl/evp.h>
19 #include <openssl/core_names.h>
20 
21 static int get_key_values(EVP_PKEY *pkey);
22 
23 /*
24  * The following code shows how to generate an EC key from a curve name
25  * with additional parameters. If only the curve name is required then the
26  * simple helper can be used instead i.e. Either
27  * pkey = EVP_EC_gen(curvename); OR
28  * pkey = EVP_PKEY_Q_keygen(libctx, propq, "EC", curvename);
29  */
30 static EVP_PKEY *do_ec_keygen(void)
31 {
32     /*
33      * The libctx and propq can be set if required, they are included here
34      * to show how they are passed to EVP_PKEY_CTX_new_from_name().
35      */
36     OSSL_LIB_CTX *libctx = NULL;
37     const char *propq = NULL;
38     EVP_PKEY *key = NULL;
39     OSSL_PARAM params[3];
40     EVP_PKEY_CTX *genctx = NULL;
41     const char *curvename = "P-256";
42     int use_cofactordh = 1;
43 
44     genctx = EVP_PKEY_CTX_new_from_name(libctx, "EC", propq);
45     if (genctx == NULL) {
46         fprintf(stderr, "EVP_PKEY_CTX_new_from_name() failed\n");
47         goto cleanup;
48     }
49 
50     if (EVP_PKEY_keygen_init(genctx) <= 0) {
51         fprintf(stderr, "EVP_PKEY_keygen_init() failed\n");
52         goto cleanup;
53     }
54 
55     params[0] = OSSL_PARAM_construct_utf8_string(OSSL_PKEY_PARAM_GROUP_NAME,
56                                                  (char *)curvename, 0);
57     /*
58      * This is an optional parameter.
59      * For many curves where the cofactor is 1, setting this has no effect.
60      */
61     params[1] = OSSL_PARAM_construct_int(OSSL_PKEY_PARAM_USE_COFACTOR_ECDH,
62                                          &use_cofactordh);
63     params[2] = OSSL_PARAM_construct_end();
64     if (!EVP_PKEY_CTX_set_params(genctx, params)) {
65         fprintf(stderr, "EVP_PKEY_CTX_set_params() failed\n");
66         goto cleanup;
67     }
68 
69     fprintf(stdout, "Generating EC key\n\n");
70     if (EVP_PKEY_generate(genctx, &key) <= 0) {
71         fprintf(stderr, "EVP_PKEY_generate() failed\n");
72         goto cleanup;
73     }
74 cleanup:
75     EVP_PKEY_CTX_free(genctx);
76     return key;
77 }
78 
79 /*
80  * The following code shows how retrieve key data from the generated
81  * EC key. See doc/man7/EVP_PKEY-EC.pod for more information.
82  *
83  * EVP_PKEY_print_private() could also be used to display the values.
84  */
85 static int get_key_values(EVP_PKEY *pkey)
86 {
87     int result = 0;
88     char out_curvename[80];
89     unsigned char out_pubkey[80];
90     unsigned char out_privkey[80];
91     BIGNUM *out_priv = NULL;
92     size_t out_pubkey_len, out_privkey_len = 0;
93 
94     if (!EVP_PKEY_get_utf8_string_param(pkey, OSSL_PKEY_PARAM_GROUP_NAME,
95                                         out_curvename, sizeof(out_curvename),
96                                         NULL)) {
97         fprintf(stderr, "Failed to get curve name\n");
98         goto cleanup;
99     }
100 
101     if (!EVP_PKEY_get_octet_string_param(pkey, OSSL_PKEY_PARAM_PUB_KEY,
102                                         out_pubkey, sizeof(out_pubkey),
103                                         &out_pubkey_len)) {
104         fprintf(stderr, "Failed to get public key\n");
105         goto cleanup;
106     }
107 
108     if (!EVP_PKEY_get_bn_param(pkey, OSSL_PKEY_PARAM_PRIV_KEY, &out_priv)) {
109         fprintf(stderr, "Failed to get private key\n");
110         goto cleanup;
111     }
112 
113     out_privkey_len = BN_bn2bin(out_priv, out_privkey);
114     if (out_privkey_len <= 0 || out_privkey_len > sizeof(out_privkey)) {
115         fprintf(stderr, "BN_bn2bin failed\n");
116         goto cleanup;
117     }
118 
119     fprintf(stdout, "Curve name: %s\n", out_curvename);
120     fprintf(stdout, "Public key:\n");
121     BIO_dump_indent_fp(stdout, out_pubkey, out_pubkey_len, 2);
122     fprintf(stdout, "Private Key:\n");
123     BIO_dump_indent_fp(stdout, out_privkey, out_privkey_len, 2);
124 
125     result = 1;
126 cleanup:
127     /* Zeroize the private key data when we free it */
128     BN_clear_free(out_priv);
129     return result;
130 }
131 
132 int main(void)
133 {
134     int result = 0;
135     EVP_PKEY *pkey;
136 
137     pkey = do_ec_keygen();
138     if (pkey == NULL)
139         goto cleanup;
140 
141     if (!get_key_values(pkey))
142         goto cleanup;
143 
144     /*
145      * At this point we can write out the generated key using
146      * i2d_PrivateKey() and i2d_PublicKey() if required.
147      */
148     result = 1;
149 cleanup:
150     if (result != 1)
151         ERR_print_errors_fp(stderr);
152 
153     EVP_PKEY_free(pkey);
154     return result == 0;
155 }
156