Enable and fix many extra warnings supported by GCC and Clang.
[tinc] / src / gcrypt / cipher.c
1 /*
2     cipher.c -- Symmetric block cipher handling
3     Copyright (C) 2007-2022 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 "cipher.h"
23 #include "../cipher.h"
24 #include "../logger.h"
25 #include "../xalloc.h"
26
27 static struct {
28         const char *name;
29         int algo;
30         int mode;
31         int nid;
32 } ciphertable[] = {
33         {"none", GCRY_CIPHER_NONE, GCRY_CIPHER_MODE_NONE, 0},
34
35         {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_ECB, 92},
36         {"blowfish", GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CBC, 91},
37         {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_CFB, 93},
38         {NULL, GCRY_CIPHER_BLOWFISH, GCRY_CIPHER_MODE_OFB, 94},
39
40         {"aes-128-ecb", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_ECB, 418},
41         {"aes-128-cbc", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CBC, 419},
42         {"aes-128-cfb", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_CFB, 421},
43         {"aes-128-ofb", GCRY_CIPHER_AES, GCRY_CIPHER_MODE_OFB, 420},
44
45         {"aes-192-ecb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_ECB, 422},
46         {"aes-192-cbc", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CBC, 423},
47         {"aes-192-cfb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_CFB, 425},
48         {"aes-192-ofb", GCRY_CIPHER_AES192, GCRY_CIPHER_MODE_OFB, 424},
49
50         {"aes-256-ecb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_ECB, 426},
51         {"aes-256-cbc", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CBC, 427},
52         {"aes-256-cfb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_CFB, 429},
53         {"aes-256-ofb", GCRY_CIPHER_AES256, GCRY_CIPHER_MODE_OFB, 428},
54 };
55
56 static bool nametocipher(const char *name, int *algo, int *mode) {
57         for(size_t i = 0; i < sizeof(ciphertable) / sizeof(*ciphertable); i++) {
58                 if(ciphertable[i].name && !strcasecmp(name, ciphertable[i].name)) {
59                         *algo = ciphertable[i].algo;
60                         *mode = ciphertable[i].mode;
61                         return true;
62                 }
63         }
64
65         return false;
66 }
67
68 static bool nidtocipher(int nid, int *algo, int *mode) {
69         for(size_t i = 0; i < sizeof(ciphertable) / sizeof(*ciphertable); i++) {
70                 if(nid == ciphertable[i].nid) {
71                         *algo = ciphertable[i].algo;
72                         *mode = ciphertable[i].mode;
73                         return true;
74                 }
75         }
76
77         return false;
78 }
79
80 static bool ciphertonid(int algo, int mode, int *nid) {
81         for(size_t i = 0; i < sizeof(ciphertable) / sizeof(*ciphertable); i++) {
82                 if(algo == ciphertable[i].algo && mode == ciphertable[i].mode) {
83                         *nid = ciphertable[i].nid;
84                         return true;
85                 }
86         }
87
88         return false;
89 }
90
91 static bool cipher_open(cipher_t *cipher, int algo, int mode) {
92         gcry_error_t err;
93
94         if(!ciphertonid(algo, mode, &cipher->nid)) {
95                 logger(DEBUG_ALWAYS, LOG_DEBUG, "Cipher %d mode %d has no corresponding nid!", algo, mode);
96                 return false;
97         }
98
99         if((err = gcry_cipher_open(&cipher->handle, algo, mode, 0))) {
100                 logger(DEBUG_ALWAYS, LOG_DEBUG, "Unable to initialise cipher %d mode %d: %s", algo, mode, gcry_strerror(err));
101                 return false;
102         }
103
104         cipher->keylen = gcry_cipher_get_algo_keylen(algo);
105         cipher->blklen = gcry_cipher_get_algo_blklen(algo);
106         cipher->key = xmalloc(cipher->keylen + cipher->blklen);
107         cipher->padding = mode == GCRY_CIPHER_MODE_ECB || mode == GCRY_CIPHER_MODE_CBC;
108
109         return true;
110 }
111
112 bool cipher_open_by_name(cipher_t *cipher, const char *name) {
113         int algo, mode;
114
115         if(!nametocipher(name, &algo, &mode)) {
116                 logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown cipher name '%s'!", name);
117                 return false;
118         }
119
120         return cipher_open(cipher, algo, mode);
121 }
122
123 bool cipher_open_by_nid(cipher_t *cipher, int nid) {
124         int algo, mode;
125
126         if(!nidtocipher(nid, &algo, &mode)) {
127                 logger(DEBUG_ALWAYS, LOG_DEBUG, "Unknown cipher ID %d!", nid);
128                 return false;
129         }
130
131         return cipher_open(cipher, algo, mode);
132 }
133
134 void cipher_close(cipher_t *cipher) {
135         if(cipher->handle) {
136                 gcry_cipher_close(cipher->handle);
137                 cipher->handle = NULL;
138         }
139
140         free(cipher->key);
141
142         memset(cipher, 0, sizeof(*cipher));
143 }
144
145 size_t cipher_keylength(const cipher_t *cipher) {
146         if(!cipher) {
147                 return 0;
148         }
149
150         return cipher->keylen + cipher->blklen;
151 }
152
153 uint64_t cipher_budget(const cipher_t *cipher) {
154         if(!cipher) {
155                 return UINT64_MAX; // NULL cipher
156         }
157
158         size_t ivlen = cipher->blklen;
159         size_t blklen = cipher->blklen;
160
161         size_t len = blklen > 1
162                      ? blklen
163                      : ivlen > 1 ? ivlen : 8;
164         size_t bits = len * 4 - 1;
165
166         return bits < 64
167                ? UINT64_C(1) << bits
168                : UINT64_MAX;
169 }
170
171 size_t cipher_blocksize(const cipher_t *cipher) {
172         if(!cipher || !cipher->blklen) {
173                 return 1;
174         }
175
176         return cipher->blklen;
177 }
178
179 bool cipher_set_key(cipher_t *cipher, void *key, bool encrypt) {
180         (void)encrypt;
181
182         memcpy(cipher->key, key, cipher->keylen + cipher->blklen);
183
184         gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
185         gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
186
187         return true;
188 }
189
190 bool cipher_set_key_from_rsa(cipher_t *cipher, void *key, size_t len, bool encrypt) {
191         (void)encrypt;
192
193         memcpy(cipher->key, (char *)key + len - cipher->keylen, cipher->keylen);
194         gcry_cipher_setkey(cipher->handle, cipher->key, cipher->keylen);
195
196         memcpy((char *)cipher->key + cipher->keylen, (char *)key + len - cipher->blklen - cipher->keylen, cipher->blklen);
197         gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
198
199         return true;
200 }
201
202 bool cipher_encrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
203         gcry_error_t err;
204         uint8_t pad[cipher->blklen];
205
206         if(cipher->padding) {
207                 if(!oneshot) {
208                         return false;
209                 }
210
211                 size_t reqlen = ((inlen + cipher->blklen) / cipher->blklen) * cipher->blklen;
212
213                 if(*outlen < reqlen) {
214                         logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: not enough room for padding");
215                         return false;
216                 }
217
218                 uint8_t padbyte = reqlen - inlen;
219                 inlen = reqlen - cipher->blklen;
220
221                 for(int i = 0; i < cipher->blklen; i++)
222                         if(i < cipher->blklen - padbyte) {
223                                 pad[i] = ((uint8_t *)indata)[inlen + i];
224                         } else {
225                                 pad[i] = padbyte;
226                         }
227         }
228
229         if(oneshot) {
230                 gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
231         }
232
233         if((err = gcry_cipher_encrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
234                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
235                 return false;
236         }
237
238         if(cipher->padding) {
239                 if((err = gcry_cipher_encrypt(cipher->handle, (char *)outdata + inlen, cipher->blklen, pad, cipher->blklen))) {
240                         logger(DEBUG_ALWAYS, LOG_ERR, "Error while encrypting: %s", gcry_strerror(err));
241                         return false;
242                 }
243
244                 inlen += cipher->blklen;
245         }
246
247         *outlen = inlen;
248         return true;
249 }
250
251 bool cipher_decrypt(cipher_t *cipher, const void *indata, size_t inlen, void *outdata, size_t *outlen, bool oneshot) {
252         gcry_error_t err;
253
254         if(oneshot) {
255                 gcry_cipher_setiv(cipher->handle, cipher->key + cipher->keylen, cipher->blklen);
256         }
257
258         if((err = gcry_cipher_decrypt(cipher->handle, outdata, *outlen, indata, inlen))) {
259                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: %s", gcry_strerror(err));
260                 return false;
261         }
262
263         if(cipher->padding) {
264                 if(!oneshot) {
265                         return false;
266                 }
267
268                 uint8_t padbyte = ((uint8_t *)outdata)[inlen - 1];
269
270                 if(padbyte == 0 || padbyte > cipher->blklen || padbyte > inlen) {
271                         logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: invalid padding");
272                         return false;
273                 }
274
275                 size_t origlen = inlen - padbyte;
276
277                 for(size_t i = inlen - 1; i >= origlen; i--)
278                         if(((uint8_t *)outdata)[i] != padbyte) {
279                                 logger(DEBUG_ALWAYS, LOG_ERR, "Error while decrypting: invalid padding");
280                                 return false;
281                         }
282
283                 *outlen = origlen;
284         } else {
285                 *outlen = inlen;
286         }
287
288         return true;
289 }
290
291 int cipher_get_nid(const cipher_t *cipher) {
292         if(!cipher || !cipher->nid) {
293                 return 0;
294         }
295
296         return cipher->nid;
297 }
298
299 bool cipher_active(const cipher_t *cipher) {
300         return cipher->nid != 0;
301 }