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