summaryrefslogtreecommitdiffstats
path: root/demos/signature/EVP_ED_Signature_demo.c
blob: e5cb6f4de15fc4ffdedd2e6b71d3dff86ea73c00 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
/*-
 * Copyright 2023 The OpenSSL Project Authors. All Rights Reserved.
 *
 * Licensed under the Apache License 2.0 (the "License").  You may not use
 * this file except in compliance with the License.  You can obtain a copy
 * in the file LICENSE in the source distribution or at
 * https://www.openssl.org/source/license.html
 */

/*
 * This demonstration will calculate and verify an ED25519 signature of
 * a message using  EVP_DigestSign() and EVP_DigestVerify().
 */

#include <string.h>
#include <stdio.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/core_names.h>

/* A test message to be signed (TBS) */
static const unsigned char hamlet[] =
    "To be, or not to be, that is the question,\n"
    "Whether tis nobler in the minde to suffer\n"
    "The slings and arrowes of outragious fortune,\n"
    "Or to take Armes again in a sea of troubles,\n";

static int 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)
{
    int ret = 0;
    size_t sig_len;
    unsigned char *sig_value = NULL;
    EVP_MD_CTX *sign_context = NULL;

    /* Create a signature context */
    sign_context = EVP_MD_CTX_new();
    if (sign_context == NULL) {
        fprintf(stderr, "EVP_MD_CTX_new failed.\n");
        goto cleanup;
    }

    /*
     * Initialize the sign context using an ED25519 private key
     * Notice that the digest name must NOT be used.
     * In this demo we don't specify any additional parameters via
     * OSSL_PARAM, which means it will use default values.
     * For more information, refer to doc/man7/EVP_SIGNATURE-ED25519.pod
     * "ED25519 and ED448 Signature Parameters"
     */
    if (!EVP_DigestSignInit_ex(sign_context, NULL, NULL, libctx, NULL, priv, NULL)) {
        fprintf(stderr, "EVP_DigestSignInit_ex failed.\n");
        goto cleanup;
    }

    /* Calculate the required size for the signature by passing a NULL buffer. */
    if (!EVP_DigestSign(sign_context, NULL, &sig_len, tbs, tbs_len)) {
        fprintf(stderr, "EVP_DigestSign using NULL buffer failed.\n");
        goto cleanup;
    }
    sig_value = OPENSSL_malloc(sig_len);
    if (sig_value == NULL) {
        fprintf(stderr, "OPENSSL_malloc failed.\n");
        goto cleanup;
    }
    fprintf(stdout, "Generating signature:\n");
    if (!EVP_DigestSign(sign_context, sig_value, &sig_len, tbs, tbs_len)) {
        fprintf(stderr, "EVP_DigestSign failed.\n");
        goto cleanup;
    }
    *sig_out_len = sig_len;
    *sig_out_value = sig_value;
    BIO_dump_indent_fp(stdout, sig_value, sig_len, 2);
    fprintf(stdout, "\n");
    ret = 1;

cleanup:
    if (!ret)
        OPENSSL_free(sig_value);
    EVP_MD_CTX_free(sign_context);
    return ret;
}

static int 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)
{
    int ret = 0;
    EVP_MD_CTX *verify_context = NULL;

    /*
     * Make a verify signature context to hold temporary state
     * during signature verification
     */
    verify_context = EVP_MD_CTX_new();
    if (verify_context == NULL) {
        fprintf(stderr, "EVP_MD_CTX_new failed.\n");
        goto cleanup;
    }
    /* Initialize the verify context with a ED25519 public key */
    if (!EVP_DigestVerifyInit_ex(verify_context, NULL, NULL,
                                 libctx, NULL, pub, NULL)) {
        fprintf(stderr, "EVP_DigestVerifyInit_ex failed.\n");
        goto cleanup;
    }
    /*
     * ED25519 only supports the one shot interface using EVP_DigestVerify()
     * The streaming EVP_DigestVerifyUpdate() API is not supported.
     */
    if (!EVP_DigestVerify(verify_context, sig_value, sig_len,
                          tbs, tbs_len)) {
        fprintf(stderr, "EVP_DigestVerify() failed.\n");
        goto cleanup;
    }
    fprintf(stdout, "Signature verified.\n");
    ret = 1;

cleanup:
    EVP_MD_CTX_free(verify_context);
    return ret;
}

static int create_key(OSSL_LIB_CTX *libctx,
                      EVP_PKEY **privout, EVP_PKEY **pubout)
{
    int ret = 0;
    EVP_PKEY *priv = NULL, *pub = NULL;
    unsigned char pubdata[32];
    size_t pubdata_len = 0;

    /*
     * In this demo we just create a keypair, and extract the
     * public key. We could also use EVP_PKEY_new_raw_private_key_ex()
     * to create a key from raw data.
     */
    priv = EVP_PKEY_Q_keygen(libctx, NULL, "ED25519");
    if (priv == NULL) {
        fprintf(stderr, "EVP_PKEY_Q_keygen() failed\n");
        goto end;
    }

    if (!EVP_PKEY_get_octet_string_param(priv,
                                         OSSL_PKEY_PARAM_PUB_KEY,
                                         pubdata,
                                         sizeof(pubdata),
                                         &pubdata_len)) {
        fprintf(stderr, "EVP_PKEY_get_octet_string_param() failed\n");
        goto end;
    }
    pub = EVP_PKEY_new_raw_public_key_ex(libctx, "ED25519", NULL, pubdata, pubdata_len);
    if (pub == NULL) {
        fprintf(stderr, "EVP_PKEY_new_raw_public_key_ex() failed\n");
        goto end;
    }
    ret = 1;
end:
    if (ret) {
        *pubout = pub;
        *privout = priv;
    } else {
        EVP_PKEY_free(priv);
    }
    return ret;
}

int main(void)
{
    OSSL_LIB_CTX *libctx = NULL;
    size_t sig_len = 0;
    unsigned char *sig_value = NULL;
    int ret = EXIT_FAILURE;
    EVP_PKEY *priv = NULL, *pub = NULL;

    libctx = OSSL_LIB_CTX_new();
    if (libctx == NULL) {
        fprintf(stderr, "OSSL_LIB_CTX_new() returned NULL\n");
        goto cleanup;
    }
    if (!create_key(libctx, &priv, &pub)) {
        fprintf(stderr, "Failed to create key.\n");
        goto cleanup;
    }

    if (!demo_sign(priv, hamlet, sizeof(hamlet), libctx,
                   &sig_value, &sig_len)) {
        fprintf(stderr, "demo_sign failed.\n");
        goto cleanup;
    }
    if (!demo_verify(pub, hamlet, sizeof(hamlet),
                     sig_value, sig_len, libctx)) {
        fprintf(stderr, "demo_verify failed.\n");
        goto cleanup;
    }
    ret = EXIT_SUCCESS;

cleanup:
    if (ret != EXIT_SUCCESS)
        ERR_print_errors_fp(stderr);
    EVP_PKEY_free(pub);
    EVP_PKEY_free(priv);
    OSSL_LIB_CTX_free(libctx);
    OPENSSL_free(sig_value);
    return ret;
}