xref: /src/crypto/openssl/demos/signature/EVP_EC_Signature_demo.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*-
2  * Copyright 2021-2023 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  * An example that uses the EVP_MD*, EVP_DigestSign* and EVP_DigestVerify*
12  * methods to calculate and verify a signature of two static buffers.
13  */
14 
15 #include <string.h>
16 #include <stdio.h>
17 #include <openssl/err.h>
18 #include <openssl/evp.h>
19 #include <openssl/decoder.h>
20 #include "EVP_EC_Signature_demo.h"
21 
22 /*
23  * This demonstration will calculate and verify a signature of data using
24  * the soliloquy from Hamlet scene 1 act 3
25  */
26 
27 static const char *hamlet_1 = "To be, or not to be, that is the question,\n"
28                               "Whether tis nobler in the minde to suffer\n"
29                               "The slings and arrowes of outragious fortune,\n"
30                               "Or to take Armes again in a sea of troubles,\n";
31 static const char *hamlet_2 = "And by opposing, end them, to die to sleep;\n"
32                               "No more, and by a sleep, to say we end\n"
33                               "The heart-ache, and the thousand natural shocks\n"
34                               "That flesh is heir to? tis a consumation\n";
35 
36 /*
37  * For demo_sign, load EC private key priv_key from priv_key_der[].
38  * For demo_verify, load EC public key pub_key from pub_key_der[].
39  */
get_key(OSSL_LIB_CTX * libctx,const char * propq,int public)40 static EVP_PKEY *get_key(OSSL_LIB_CTX *libctx, const char *propq, int public)
41 {
42     OSSL_DECODER_CTX *dctx = NULL;
43     EVP_PKEY *pkey = NULL;
44     int selection;
45     const unsigned char *data;
46     size_t data_len;
47 
48     if (public) {
49         selection = EVP_PKEY_PUBLIC_KEY;
50         data = pub_key_der;
51         data_len = sizeof(pub_key_der);
52     } else {
53         selection = EVP_PKEY_KEYPAIR;
54         data = priv_key_der;
55         data_len = sizeof(priv_key_der);
56     }
57     dctx = OSSL_DECODER_CTX_new_for_pkey(&pkey, "DER", NULL, "EC",
58         selection, libctx, propq);
59     (void)OSSL_DECODER_from_data(dctx, &data, &data_len);
60     OSSL_DECODER_CTX_free(dctx);
61     if (pkey == NULL)
62         fprintf(stderr, "Failed to load %s key.\n", public ? "public" : "private");
63     return pkey;
64 }
65 
demo_sign(OSSL_LIB_CTX * libctx,const char * sig_name,size_t * sig_out_len,unsigned char ** sig_out_value)66 static int demo_sign(OSSL_LIB_CTX *libctx, const char *sig_name,
67     size_t *sig_out_len, unsigned char **sig_out_value)
68 {
69     int ret = 0, public = 0;
70     size_t sig_len;
71     unsigned char *sig_value = NULL;
72     const char *propq = NULL;
73     EVP_MD_CTX *sign_context = NULL;
74     EVP_PKEY *priv_key = NULL;
75 
76     /* Get private key */
77     priv_key = get_key(libctx, propq, public);
78     if (priv_key == NULL) {
79         fprintf(stderr, "Get private key failed.\n");
80         goto cleanup;
81     }
82     /*
83      * Make a message signature context to hold temporary state
84      * during signature creation
85      */
86     sign_context = EVP_MD_CTX_new();
87     if (sign_context == NULL) {
88         fprintf(stderr, "EVP_MD_CTX_new failed.\n");
89         goto cleanup;
90     }
91     /*
92      * Initialize the sign context to use the fetched
93      * sign provider.
94      */
95     if (!EVP_DigestSignInit_ex(sign_context, NULL, sig_name,
96             libctx, NULL, priv_key, NULL)) {
97         fprintf(stderr, "EVP_DigestSignInit_ex failed.\n");
98         goto cleanup;
99     }
100     /*
101      * EVP_DigestSignUpdate() can be called several times on the same context
102      * to include additional data.
103      */
104     if (!EVP_DigestSignUpdate(sign_context, hamlet_1, strlen(hamlet_1))) {
105         fprintf(stderr, "EVP_DigestSignUpdate(hamlet_1) failed.\n");
106         goto cleanup;
107     }
108     if (!EVP_DigestSignUpdate(sign_context, hamlet_2, strlen(hamlet_2))) {
109         fprintf(stderr, "EVP_DigestSignUpdate(hamlet_2) failed.\n");
110         goto cleanup;
111     }
112     /* Call EVP_DigestSignFinal to get signature length sig_len */
113     if (!EVP_DigestSignFinal(sign_context, NULL, &sig_len)) {
114         fprintf(stderr, "EVP_DigestSignFinal failed.\n");
115         goto cleanup;
116     }
117     if (sig_len <= 0) {
118         fprintf(stderr, "EVP_DigestSignFinal returned invalid signature length.\n");
119         goto cleanup;
120     }
121     sig_value = OPENSSL_malloc(sig_len);
122     if (sig_value == NULL) {
123         fprintf(stderr, "No memory.\n");
124         goto cleanup;
125     }
126     if (!EVP_DigestSignFinal(sign_context, sig_value, &sig_len)) {
127         fprintf(stderr, "EVP_DigestSignFinal failed.\n");
128         goto cleanup;
129     }
130     *sig_out_len = sig_len;
131     *sig_out_value = sig_value;
132     fprintf(stdout, "Generating signature:\n");
133     BIO_dump_indent_fp(stdout, sig_value, sig_len, 2);
134     fprintf(stdout, "\n");
135     ret = 1;
136 
137 cleanup:
138     /* OpenSSL free functions will ignore NULL arguments */
139     if (!ret)
140         OPENSSL_free(sig_value);
141     EVP_PKEY_free(priv_key);
142     EVP_MD_CTX_free(sign_context);
143     return ret;
144 }
145 
demo_verify(OSSL_LIB_CTX * libctx,const char * sig_name,size_t sig_len,unsigned char * sig_value)146 static int demo_verify(OSSL_LIB_CTX *libctx, const char *sig_name,
147     size_t sig_len, unsigned char *sig_value)
148 {
149     int ret = 0, public = 1;
150     const char *propq = NULL;
151     EVP_MD_CTX *verify_context = NULL;
152     EVP_PKEY *pub_key = NULL;
153 
154     /*
155      * Make a verify signature context to hold temporary state
156      * during signature verification
157      */
158     verify_context = EVP_MD_CTX_new();
159     if (verify_context == NULL) {
160         fprintf(stderr, "EVP_MD_CTX_new failed.\n");
161         goto cleanup;
162     }
163     /* Get public key */
164     pub_key = get_key(libctx, propq, public);
165     if (pub_key == NULL) {
166         fprintf(stderr, "Get public key failed.\n");
167         goto cleanup;
168     }
169     /* Verify */
170     if (!EVP_DigestVerifyInit_ex(verify_context, NULL, sig_name,
171             libctx, NULL, pub_key, NULL)) {
172         fprintf(stderr, "EVP_DigestVerifyInit failed.\n");
173         goto cleanup;
174     }
175     /*
176      * EVP_DigestVerifyUpdate() can be called several times on the same context
177      * to include additional data.
178      */
179     if (!EVP_DigestVerifyUpdate(verify_context, hamlet_1, strlen(hamlet_1))) {
180         fprintf(stderr, "EVP_DigestVerifyUpdate(hamlet_1) failed.\n");
181         goto cleanup;
182     }
183     if (!EVP_DigestVerifyUpdate(verify_context, hamlet_2, strlen(hamlet_2))) {
184         fprintf(stderr, "EVP_DigestVerifyUpdate(hamlet_2) failed.\n");
185         goto cleanup;
186     }
187     if (EVP_DigestVerifyFinal(verify_context, sig_value, sig_len) <= 0) {
188         fprintf(stderr, "EVP_DigestVerifyFinal failed.\n");
189         goto cleanup;
190     }
191     fprintf(stdout, "Signature verified.\n");
192     ret = 1;
193 
194 cleanup:
195     /* OpenSSL free functions will ignore NULL arguments */
196     EVP_PKEY_free(pub_key);
197     EVP_MD_CTX_free(verify_context);
198     return ret;
199 }
200 
main(void)201 int main(void)
202 {
203     OSSL_LIB_CTX *libctx = NULL;
204     const char *sig_name = "SHA3-512";
205     size_t sig_len = 0;
206     unsigned char *sig_value = NULL;
207     int ret = EXIT_FAILURE;
208 
209     libctx = OSSL_LIB_CTX_new();
210     if (libctx == NULL) {
211         fprintf(stderr, "OSSL_LIB_CTX_new() returned NULL\n");
212         goto cleanup;
213     }
214     if (!demo_sign(libctx, sig_name, &sig_len, &sig_value)) {
215         fprintf(stderr, "demo_sign failed.\n");
216         goto cleanup;
217     }
218     if (!demo_verify(libctx, sig_name, sig_len, sig_value)) {
219         fprintf(stderr, "demo_verify failed.\n");
220         goto cleanup;
221     }
222     ret = EXIT_SUCCESS;
223 
224 cleanup:
225     if (ret != EXIT_SUCCESS)
226         ERR_print_errors_fp(stderr);
227     /* OpenSSL free functions will ignore NULL arguments */
228     OSSL_LIB_CTX_free(libctx);
229     OPENSSL_free(sig_value);
230     return ret;
231 }
232