summaryrefslogtreecommitdiffstats
path: root/demos/signature/rsa_pss_hash.c
blob: 71f50cab802c9924cf2dc6b8db747f4ed2903c12 (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
/*
 * Copyright 2022 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
 */

#include <stdio.h>
#include <stdlib.h>
#include <openssl/core_names.h>
#include <openssl/evp.h>
#include <openssl/rsa.h>
#include <openssl/params.h>
#include <openssl/err.h>
#include <openssl/bio.h>
#include "rsa_pss.h"

/* The data to be signed. This will be hashed. */
static const char test_message[] =
    "This is an example message to be signed.";

/* A property query used for selecting algorithm implementations. */
static const char *propq = NULL;

/*
 * This function demonstrates RSA signing of an arbitrary-length message.
 * Hashing is performed automatically. In this example, SHA-256 is used. If you
 * have already hashed your message and simply want to sign the hash directly,
 * see rsa_pss_direct.c.
 */
static int sign(OSSL_LIB_CTX *libctx, unsigned char **sig, size_t *sig_len)
{
    int ret = 0;
    EVP_PKEY *pkey = NULL;
    EVP_MD_CTX *mctx = NULL;
    OSSL_PARAM params[2], *p = params;
    const unsigned char *ppriv_key = NULL;

    *sig = NULL;

    /* Load DER-encoded RSA private key. */
    ppriv_key = rsa_priv_key;
    pkey = d2i_PrivateKey_ex(EVP_PKEY_RSA, NULL, &ppriv_key,
                             sizeof(rsa_priv_key), libctx, propq);
    if (pkey == NULL) {
        fprintf(stderr, "Failed to load private key\n");
        goto end;
    }

    /* Create MD context used for signing. */
    mctx = EVP_MD_CTX_new();
    if (mctx == NULL) {
        fprintf(stderr, "Failed to create MD context\n");
        goto end;
    }

    /* Initialize MD context for signing. */
    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE,
                                            OSSL_PKEY_RSA_PAD_MODE_PSS, 0);
    *p = OSSL_PARAM_construct_end();

    if (EVP_DigestSignInit_ex(mctx, NULL, "SHA256", libctx, propq,
                              pkey, params) == 0) {
        fprintf(stderr, "Failed to initialize signing context\n");
        goto end;
    }

    /*
     * Feed data to be signed into the algorithm. This may
     * be called multiple times.
     */
    if (EVP_DigestSignUpdate(mctx, test_message, sizeof(test_message)) == 0) {
        fprintf(stderr, "Failed to hash message into signing context\n");
        goto end;
    }

    /* Determine signature length. */
    if (EVP_DigestSignFinal(mctx, NULL, sig_len) == 0) {
        fprintf(stderr, "Failed to get signature length\n");
        goto end;
    }

    /* Allocate memory for signature. */
    *sig = OPENSSL_malloc(*sig_len);
    if (*sig == NULL) {
        fprintf(stderr, "Failed to allocate memory for signature\n");
        goto end;
    }

    /* Generate signature. */
    if (EVP_DigestSignFinal(mctx, *sig, sig_len) == 0) {
        fprintf(stderr, "Failed to sign\n");
        goto end;
    }

    ret = 1;
end:
    EVP_MD_CTX_free(mctx);
    EVP_PKEY_free(pkey);

    if (ret == 0)
        OPENSSL_free(*sig);

    return ret;
}

/*
 * This function demonstrates verification of an RSA signature over an
 * arbitrary-length message using the PSS signature scheme. Hashing is performed
 * automatically.
 */
static int verify(OSSL_LIB_CTX *libctx, const unsigned char *sig, size_t sig_len)
{
    int ret = 0;
    EVP_PKEY *pkey = NULL;
    EVP_MD_CTX *mctx = NULL;
    OSSL_PARAM params[2], *p = params;
    const unsigned char *ppub_key = NULL;

    /* Load DER-encoded RSA public key. */
    ppub_key = rsa_pub_key;
    pkey = d2i_PublicKey(EVP_PKEY_RSA, NULL, &ppub_key, sizeof(rsa_pub_key));
    if (pkey == NULL) {
        fprintf(stderr, "Failed to load public key\n");
        goto end;
    }

    /* Create MD context used for verification. */
    mctx = EVP_MD_CTX_new();
    if (mctx == NULL) {
        fprintf(stderr, "Failed to create MD context\n");
        goto end;
    }

    /* Initialize MD context for verification. */
    *p++ = OSSL_PARAM_construct_utf8_string(OSSL_SIGNATURE_PARAM_PAD_MODE,
                                            OSSL_PKEY_RSA_PAD_MODE_PSS, 0);
    *p = OSSL_PARAM_construct_end();

    if (EVP_DigestVerifyInit_ex(mctx, NULL, "SHA256", libctx, propq,
                                pkey, params) == 0) {
        fprintf(stderr, "Failed to initialize signing context\n");
        goto end;
    }

    /*
     * Feed data to be signed into the algorithm. This may
     * be called multiple times.
     */
    if (EVP_DigestVerifyUpdate(mctx, test_message, sizeof(test_message)) == 0) {
        fprintf(stderr, "Failed to hash message into signing context\n");
        goto end;
    }

    /* Verify signature. */
    if (EVP_DigestVerifyFinal(mctx, sig, sig_len) == 0) {
        fprintf(stderr, "Failed to verify signature; "
                "signature may be invalid\n");
        goto end;
    }

    ret = 1;
end:
    EVP_MD_CTX_free(mctx);
    EVP_PKEY_free(pkey);
    return ret;
}

int main(int argc, char **argv)
{
    int ret = EXIT_FAILURE;
    OSSL_LIB_CTX *libctx = NULL;
    unsigned char *sig = NULL;
    size_t sig_len = 0;

    if (sign(libctx, &sig, &sig_len) == 0)
        goto end;

    if (verify(libctx, sig, sig_len) == 0)
        goto end;

    ret = EXIT_SUCCESS;
end:
    OPENSSL_free(sig);
    OSSL_LIB_CTX_free(libctx);
    return ret;
}