xref: /src/crypto/openssl/demos/signature/EVP_ED_Signature_demo.c (revision f25b8c9fb4f58cf61adb47d7570abe7caa6d385d)
1 /*-
2  * Copyright 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  * This demonstration will calculate and verify an ED25519 signature of
12  * a message using  EVP_DigestSign() and EVP_DigestVerify().
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 /* A test message to be signed (TBS) */
22 static const unsigned char hamlet[] = "To be, or not to be, that is the question,\n"
23                                       "Whether tis nobler in the minde to suffer\n"
24                                       "The slings and arrowes of outragious fortune,\n"
25                                       "Or to take Armes again in a sea of troubles,\n";
26 
demo_sign(EVP_PKEY * priv,const unsigned char * tbs,size_t tbs_len,OSSL_LIB_CTX * libctx,unsigned char ** sig_out_value,size_t * sig_out_len)27 static int demo_sign(EVP_PKEY *priv,
28     const unsigned char *tbs, size_t tbs_len,
29     OSSL_LIB_CTX *libctx,
30     unsigned char **sig_out_value,
31     size_t *sig_out_len)
32 {
33     int ret = 0;
34     size_t sig_len;
35     unsigned char *sig_value = NULL;
36     EVP_MD_CTX *sign_context = NULL;
37 
38     /* Create a signature context */
39     sign_context = EVP_MD_CTX_new();
40     if (sign_context == NULL) {
41         fprintf(stderr, "EVP_MD_CTX_new failed.\n");
42         goto cleanup;
43     }
44 
45     /*
46      * Initialize the sign context using an ED25519 private key
47      * Notice that the digest name must NOT be used.
48      * In this demo we don't specify any additional parameters via
49      * OSSL_PARAM, which means it will use default values.
50      * For more information, refer to doc/man7/EVP_SIGNATURE-ED25519.pod
51      * "ED25519 and ED448 Signature Parameters"
52      */
53     if (!EVP_DigestSignInit_ex(sign_context, NULL, NULL, libctx, NULL, priv, NULL)) {
54         fprintf(stderr, "EVP_DigestSignInit_ex failed.\n");
55         goto cleanup;
56     }
57 
58     /* Calculate the required size for the signature by passing a NULL buffer. */
59     if (!EVP_DigestSign(sign_context, NULL, &sig_len, tbs, tbs_len)) {
60         fprintf(stderr, "EVP_DigestSign using NULL buffer failed.\n");
61         goto cleanup;
62     }
63     sig_value = OPENSSL_malloc(sig_len);
64     if (sig_value == NULL) {
65         fprintf(stderr, "OPENSSL_malloc failed.\n");
66         goto cleanup;
67     }
68     fprintf(stdout, "Generating signature:\n");
69     if (!EVP_DigestSign(sign_context, sig_value, &sig_len, tbs, tbs_len)) {
70         fprintf(stderr, "EVP_DigestSign failed.\n");
71         goto cleanup;
72     }
73     *sig_out_len = sig_len;
74     *sig_out_value = sig_value;
75     BIO_dump_indent_fp(stdout, sig_value, sig_len, 2);
76     fprintf(stdout, "\n");
77     ret = 1;
78 
79 cleanup:
80     if (!ret)
81         OPENSSL_free(sig_value);
82     EVP_MD_CTX_free(sign_context);
83     return ret;
84 }
85 
demo_verify(EVP_PKEY * pub,const unsigned char * tbs,size_t tbs_len,const unsigned char * sig_value,size_t sig_len,OSSL_LIB_CTX * libctx)86 static int demo_verify(EVP_PKEY *pub,
87     const unsigned char *tbs, size_t tbs_len,
88     const unsigned char *sig_value, size_t sig_len,
89     OSSL_LIB_CTX *libctx)
90 {
91     int ret = 0;
92     EVP_MD_CTX *verify_context = NULL;
93 
94     /*
95      * Make a verify signature context to hold temporary state
96      * during signature verification
97      */
98     verify_context = EVP_MD_CTX_new();
99     if (verify_context == NULL) {
100         fprintf(stderr, "EVP_MD_CTX_new failed.\n");
101         goto cleanup;
102     }
103     /* Initialize the verify context with a ED25519 public key */
104     if (!EVP_DigestVerifyInit_ex(verify_context, NULL, NULL,
105             libctx, NULL, pub, NULL)) {
106         fprintf(stderr, "EVP_DigestVerifyInit_ex failed.\n");
107         goto cleanup;
108     }
109     /*
110      * ED25519 only supports the one shot interface using EVP_DigestVerify()
111      * The streaming EVP_DigestVerifyUpdate() API is not supported.
112      */
113     if (!EVP_DigestVerify(verify_context, sig_value, sig_len,
114             tbs, tbs_len)) {
115         fprintf(stderr, "EVP_DigestVerify() failed.\n");
116         goto cleanup;
117     }
118     fprintf(stdout, "Signature verified.\n");
119     ret = 1;
120 
121 cleanup:
122     EVP_MD_CTX_free(verify_context);
123     return ret;
124 }
125 
create_key(OSSL_LIB_CTX * libctx,EVP_PKEY ** privout,EVP_PKEY ** pubout)126 static int create_key(OSSL_LIB_CTX *libctx,
127     EVP_PKEY **privout, EVP_PKEY **pubout)
128 {
129     int ret = 0;
130     EVP_PKEY *priv = NULL, *pub = NULL;
131     unsigned char pubdata[32];
132     size_t pubdata_len = 0;
133 
134     /*
135      * In this demo we just create a keypair, and extract the
136      * public key. We could also use EVP_PKEY_new_raw_private_key_ex()
137      * to create a key from raw data.
138      */
139     priv = EVP_PKEY_Q_keygen(libctx, NULL, "ED25519");
140     if (priv == NULL) {
141         fprintf(stderr, "EVP_PKEY_Q_keygen() failed\n");
142         goto end;
143     }
144 
145     if (!EVP_PKEY_get_octet_string_param(priv,
146             OSSL_PKEY_PARAM_PUB_KEY,
147             pubdata,
148             sizeof(pubdata),
149             &pubdata_len)) {
150         fprintf(stderr, "EVP_PKEY_get_octet_string_param() failed\n");
151         goto end;
152     }
153     pub = EVP_PKEY_new_raw_public_key_ex(libctx, "ED25519", NULL, pubdata, pubdata_len);
154     if (pub == NULL) {
155         fprintf(stderr, "EVP_PKEY_new_raw_public_key_ex() failed\n");
156         goto end;
157     }
158     ret = 1;
159 end:
160     if (ret) {
161         *pubout = pub;
162         *privout = priv;
163     } else {
164         EVP_PKEY_free(priv);
165     }
166     return ret;
167 }
168 
main(void)169 int main(void)
170 {
171     OSSL_LIB_CTX *libctx = NULL;
172     size_t sig_len = 0;
173     unsigned char *sig_value = NULL;
174     int ret = EXIT_FAILURE;
175     EVP_PKEY *priv = NULL, *pub = NULL;
176 
177     libctx = OSSL_LIB_CTX_new();
178     if (libctx == NULL) {
179         fprintf(stderr, "OSSL_LIB_CTX_new() returned NULL\n");
180         goto cleanup;
181     }
182     if (!create_key(libctx, &priv, &pub)) {
183         fprintf(stderr, "Failed to create key.\n");
184         goto cleanup;
185     }
186 
187     if (!demo_sign(priv, hamlet, sizeof(hamlet), libctx,
188             &sig_value, &sig_len)) {
189         fprintf(stderr, "demo_sign failed.\n");
190         goto cleanup;
191     }
192     if (!demo_verify(pub, hamlet, sizeof(hamlet),
193             sig_value, sig_len, libctx)) {
194         fprintf(stderr, "demo_verify failed.\n");
195         goto cleanup;
196     }
197     ret = EXIT_SUCCESS;
198 
199 cleanup:
200     if (ret != EXIT_SUCCESS)
201         ERR_print_errors_fp(stderr);
202     EVP_PKEY_free(pub);
203     EVP_PKEY_free(priv);
204     OSSL_LIB_CTX_free(libctx);
205     OPENSSL_free(sig_value);
206     return ret;
207 }
208