Add support for OpenSSL 3.0+
[tinc] / src / openssl / digest.c
1 /*
2     digest.c -- Digest handling
3     Copyright (C) 2007-2016 Guus Sliepen <guus@tinc-vpn.org>
4
5     This program is free software; you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation; either version 2 of the License, or
8     (at your option) any later version.
9
10     This program is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License along
16     with this program; if not, write to the Free Software Foundation, Inc.,
17     51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
18 */
19
20 #include "../system.h"
21
22 #include <openssl/err.h>
23 #include <openssl/hmac.h>
24
25 #if OPENSSL_VERSION_MAJOR >= 3
26 #include <openssl/core_names.h>
27 #endif
28
29 #include "digest.h"
30 #include "../digest.h"
31 #include "../logger.h"
32 #include "log.h"
33
34 static void digest_open(digest_t *digest, const EVP_MD *evp_md, size_t maclength) {
35         digest->digest = evp_md;
36
37         size_t digestlen = EVP_MD_size(digest->digest);
38
39         if(maclength == DIGEST_ALGO_SIZE || maclength > digestlen) {
40                 digest->maclength = digestlen;
41         } else {
42                 digest->maclength = maclength;
43         }
44 }
45
46 bool digest_open_by_name(digest_t *digest, const char *name, size_t maclength) {
47         const EVP_MD *evp_md = EVP_get_digestbyname(name);
48
49         if(!evp_md) {
50                 logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest name '%s'!", name);
51                 return false;
52         }
53
54         digest_open(digest, evp_md, maclength);
55         return true;
56 }
57
58 bool digest_open_by_nid(digest_t *digest, int nid, size_t maclength) {
59         const EVP_MD *evp_md = EVP_get_digestbynid(nid);
60
61         if(!evp_md) {
62                 logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown digest nid %d!", nid);
63                 return false;
64         }
65
66         digest_open(digest, evp_md, maclength);
67         return true;
68 }
69
70 bool digest_set_key(digest_t *digest, const void *key, size_t len) {
71 #if OPENSSL_VERSION_MAJOR < 3
72         digest->hmac_ctx = HMAC_CTX_new();
73
74         if(!digest->hmac_ctx) {
75                 abort();
76         }
77
78         HMAC_Init_ex(digest->hmac_ctx, key, (int)len, digest->digest, NULL);
79 #else
80         EVP_MAC *mac = EVP_MAC_fetch(NULL, OSSL_MAC_NAME_HMAC, NULL);
81
82         if(!mac) {
83                 openssl_err("fetch MAC");
84                 return false;
85         }
86
87         digest->hmac_ctx = EVP_MAC_CTX_new(mac);
88         EVP_MAC_free(mac);
89
90         if(!digest->hmac_ctx) {
91                 openssl_err("create MAC context");
92                 return false;
93         }
94
95         const char *hmac_algo = EVP_MD_get0_name(digest->digest);
96
97         if(!hmac_algo) {
98                 openssl_err("get HMAC algorithm name");
99                 return false;
100         }
101
102         // The casts are okay, the parameters are not going to change. For example, see:
103         // https://github.com/openssl/openssl/blob/31b7f23d2f958491d46c8a8e61c2b77b1b546f3e/crypto/ec/ecdh_kdf.c#L37-L38
104         const OSSL_PARAM params[] = {
105                 OSSL_PARAM_octet_string(OSSL_MAC_PARAM_KEY, (void *)key, len),
106                 OSSL_PARAM_utf8_string(OSSL_MAC_PARAM_DIGEST, (void *)hmac_algo, 0),
107                 OSSL_PARAM_END,
108         };
109
110         if(!EVP_MAC_init(digest->hmac_ctx, NULL, 0, params)) {
111                 openssl_err("set MAC context params");
112                 return false;
113         }
114
115 #endif
116         return true;
117 }
118
119 void digest_close(digest_t *digest) {
120         if(!digest) {
121                 return;
122         }
123
124         if(digest->md_ctx) {
125                 EVP_MD_CTX_destroy(digest->md_ctx);
126         }
127
128         if(digest->hmac_ctx) {
129 #if OPENSSL_VERSION_MAJOR < 3
130                 HMAC_CTX_free(digest->hmac_ctx);
131 #else
132                 EVP_MAC_CTX_free(digest->hmac_ctx);
133 #endif
134         }
135
136         memset(digest, 0, sizeof(*digest));
137 }
138
139 bool digest_create(digest_t *digest, const void *indata, size_t inlen, void *outdata) {
140         size_t len = EVP_MD_size(digest->digest);
141         unsigned char tmpdata[len];
142
143         if(digest->hmac_ctx) {
144                 bool ok;
145
146 #if OPENSSL_VERSION_MAJOR < 3
147                 ok = HMAC_Init_ex(digest->hmac_ctx, NULL, 0, NULL, NULL)
148                      && HMAC_Update(digest->hmac_ctx, indata, inlen)
149                      && HMAC_Final(digest->hmac_ctx, tmpdata, NULL);
150 #else
151                 EVP_MAC_CTX *mac_ctx = EVP_MAC_CTX_dup(digest->hmac_ctx);
152
153                 ok = mac_ctx
154                      && EVP_MAC_update(mac_ctx, indata, inlen)
155                      && EVP_MAC_final(mac_ctx, tmpdata, NULL, sizeof(tmpdata));
156
157                 EVP_MAC_CTX_free(mac_ctx);
158 #endif
159
160                 if(!ok) {
161                         openssl_err("create HMAC");
162                         return false;
163                 }
164         } else {
165                 if(!digest->md_ctx) {
166                         digest->md_ctx = EVP_MD_CTX_create();
167                 }
168
169                 if(!digest->md_ctx) {
170                         abort();
171                 }
172
173                 if(!EVP_DigestInit(digest->md_ctx, digest->digest)
174                                 || !EVP_DigestUpdate(digest->md_ctx, indata, inlen)
175                                 || !EVP_DigestFinal(digest->md_ctx, tmpdata, NULL)) {
176                         openssl_err("create digest");
177                         return false;
178                 }
179         }
180
181         memcpy(outdata, tmpdata, digest->maclength);
182         return true;
183 }
184
185 bool digest_verify(digest_t *digest, const void *indata, size_t inlen, const void *cmpdata) {
186         size_t len = digest->maclength;
187         unsigned char outdata[len];
188
189         return digest_create(digest, indata, inlen, outdata) && !memcmp(cmpdata, outdata, digest->maclength);
190 }
191
192 int digest_get_nid(const digest_t *digest) {
193         if(!digest || !digest->digest) {
194                 return 0;
195         }
196
197         return EVP_MD_type(digest->digest);
198 }
199
200 size_t digest_keylength(const digest_t *digest) {
201         if(!digest || !digest->digest) {
202                 return 0;
203         }
204
205         return EVP_MD_size(digest->digest);
206 }
207
208 size_t digest_length(const digest_t *digest) {
209         if(!digest) {
210                 return 0;
211         }
212
213         return digest->maclength;
214 }
215
216 bool digest_active(const digest_t *digest) {
217         return digest && digest->digest && EVP_MD_type(digest->digest) != 0;
218 }