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