]> git.tuebingen.mpg.de Git - paraslash.git/blob - openssl.c
openssl: Introduce openssl_perror().
[paraslash.git] / openssl.c
1 /* Copyright (C) 2005 Andre Noll <maan@tuebingen.mpg.de>, see file COPYING. */
2
3 /** \file openssl.c Openssl-based encryption/decryption routines. */
4
5 #include <regex.h>
6 #include <sys/types.h>
7 #include <sys/socket.h>
8 #include <openssl/rand.h>
9 #include <openssl/err.h>
10 #include <openssl/pem.h>
11 #include <openssl/sha.h>
12 #include <openssl/bn.h>
13 #include <openssl/aes.h>
14 #include <openssl/evp.h>
15
16 #include "para.h"
17 #include "error.h"
18 #include "string.h"
19 #include "crypt.h"
20 #include "crypt_backend.h"
21 #include "portable_io.h"
22
23 struct asymmetric_key {
24         RSA *rsa;
25 };
26
27 static int openssl_perror(const char *pfx)
28 {
29         unsigned long err = ERR_get_error();
30         PARA_ERROR_LOG("%s: \"%s\"\n", pfx, ERR_reason_error_string(err));
31         return -E_OPENSSL;
32 }
33
34 void get_random_bytes_or_die(unsigned char *buf, int num)
35 {
36         int ret;
37
38         if (RAND_bytes(buf, num) == 1) /* success */
39                 return;
40         ret = openssl_perror("RAND_bytes");
41         PARA_EMERG_LOG("%s\n", strerror(-ret));
42         exit(EXIT_FAILURE);
43 }
44
45 /*
46  * Read 64 bytes from /dev/urandom and add them to the SSL PRNG. Then seed the
47  * PRNG used by random(3) with a random seed obtained from SSL.
48  */
49 void crypt_init(void)
50 {
51         int seed, ret = RAND_load_file("/dev/urandom", 64);
52
53         if (ret != 64) {
54                 PARA_EMERG_LOG("could not seed PRNG (ret = %d)\n", ret);
55                 exit(EXIT_FAILURE);
56         }
57         get_random_bytes_or_die((unsigned char *)&seed, sizeof(seed));
58         srandom(seed);
59 }
60
61 void crypt_shutdown(void)
62 {
63 #ifdef HAVE_CRYPTO_CLEANUP_ALL_EX_DATA
64         CRYPTO_cleanup_all_ex_data();
65 #endif
66 #ifdef HAVE_OPENSSL_THREAD_STOP /* openssl-1.1 or later */
67         OPENSSL_thread_stop();
68 #else /* openssl-1.0 */
69         ERR_remove_thread_state(NULL);
70 #endif
71         EVP_cleanup();
72 }
73
74 /*
75  * The public key loading functions below were inspired by corresponding code
76  * of openssh-5.2p1, Copyright (c) 1995 Tatu Ylonen <ylo@cs.hut.fi>, Espoo,
77  * Finland. However, not much of the original code remains.
78  */
79
80 static int read_bignum(const unsigned char *buf, size_t len, BIGNUM **result)
81 {
82         const unsigned char *p = buf, *end = buf + len;
83         uint32_t bnsize;
84         BIGNUM *bn;
85
86         if (p + 4 < p)
87                 return -E_BIGNUM;
88         if (p + 4 > end)
89                 return -E_BIGNUM;
90         bnsize = read_u32_be(p);
91         PARA_DEBUG_LOG("bnsize: %u\n", bnsize);
92         p += 4;
93         if (p + bnsize < p)
94                 return -E_BIGNUM;
95         if (p + bnsize > end)
96                 return -E_BIGNUM;
97         if (bnsize > 8192)
98                 return -E_BIGNUM;
99         bn = BN_bin2bn(p, bnsize, NULL);
100         if (!bn)
101                 return -E_BIGNUM;
102         *result = bn;
103         return bnsize + 4;
104 }
105
106 static int read_public_key(const unsigned char *blob, int blen,
107                 struct asymmetric_key *result)
108 {
109         int ret;
110         RSA *rsa;
111         BIGNUM *n, *e;
112         const unsigned char *p = blob, *end = blob + blen;
113
114         rsa = RSA_new();
115         if (!rsa)
116                 return -E_BIGNUM;
117         ret = read_bignum(p, end - p, &e);
118         if (ret < 0)
119                 goto free_rsa;
120         p += ret;
121         ret = read_bignum(p, end - p, &n);
122         if (ret < 0)
123                 goto free_e;
124 #ifdef HAVE_RSA_SET0_KEY
125         RSA_set0_key(rsa, n, e, NULL);
126 #else
127         rsa->n = n;
128         rsa->e = e;
129 #endif
130         result->rsa = rsa;
131         return 1;
132 free_e:
133         BN_free(e);
134 free_rsa:
135         RSA_free(rsa);
136         return ret;
137 }
138
139 static int read_pem_private_key(const char *path, struct asymmetric_key *priv)
140 {
141         EVP_PKEY *pkey;
142         BIO *bio = BIO_new(BIO_s_file());
143
144         priv->rsa = NULL;
145         if (!bio)
146                 return -E_PRIVATE_KEY;
147         if (BIO_read_filename(bio, path) <= 0)
148                 goto bio_free;
149         pkey = PEM_read_bio_PrivateKey(bio, NULL, NULL, NULL);
150         if (!pkey)
151                 goto bio_free;
152         priv->rsa = EVP_PKEY_get1_RSA(pkey);
153         EVP_PKEY_free(pkey);
154 bio_free:
155         BIO_free(bio);
156         return priv->rsa? RSA_size(priv->rsa) : -E_PRIVATE_KEY;
157 }
158
159 static int read_openssh_private_key(const unsigned char *blob,
160                 const unsigned char *end, struct asymmetric_key *priv)
161 {
162         int ret;
163         RSA *rsa;
164         BIGNUM *n, *e, *d, *iqmp, *p, *q; /* stored in the key file */
165         const unsigned char *cp = blob;
166
167         rsa = RSA_new();
168         if (!rsa)
169                 return -E_BIGNUM;
170         ret = read_bignum(cp, end - cp, &n);
171         if (ret < 0)
172                 goto free_rsa;
173         cp += ret;
174         ret = read_bignum(cp, end - cp, &e);
175         if (ret < 0)
176                 goto free_n;
177         cp += ret;
178         ret = read_bignum(cp, end - cp, &d);
179         if (ret < 0)
180                 goto free_e;
181         cp += ret;
182         ret = read_bignum(cp, end - cp, &iqmp);
183         if (ret < 0)
184                 goto free_d;
185         cp += ret;
186         ret = read_bignum(cp, end - cp, &p);
187         if (ret < 0)
188                 goto free_iqmp;
189         cp += ret;
190         ret = read_bignum(cp, end - cp, &q);
191         if (ret < 0)
192                 goto free_p;
193 #ifdef HAVE_RSA_SET0_KEY
194         RSA_set0_key(rsa, n, e, d);
195         RSA_set0_factors(rsa, p, q);
196         RSA_set0_crt_params(rsa, NULL, NULL, iqmp);
197
198 #else
199         rsa->n = n;
200         rsa->e = e;
201         rsa->d = d;
202         rsa->iqmp = iqmp;
203         rsa->p = p;
204         rsa->q = q;
205 #endif
206         priv->rsa = rsa;
207         return 1;
208 free_p:
209         BN_clear_free(p);
210 free_iqmp:
211         BN_clear_free(iqmp);
212 free_d:
213         BN_clear_free(d);
214 free_e:
215         BN_free(e);
216 free_n:
217         BN_free(n);
218 free_rsa:
219         RSA_free(rsa);
220         return ret;
221 }
222
223 static int get_private_key(const char *path, struct asymmetric_key *priv)
224 {
225         int ret;
226         unsigned char *blob, *end;
227         size_t blob_size;
228
229         priv->rsa = NULL;
230         ret = decode_private_key(path, &blob, &blob_size);
231         if (ret < 0)
232                 return ret;
233         end = blob + blob_size;
234         if (ret == PKT_OPENSSH) {
235                 ret = find_openssh_bignum_offset(blob, blob_size);
236                 if (ret < 0)
237                         goto free_blob;
238                 PARA_INFO_LOG("reading RSA params at offset %d\n", ret);
239                 ret = read_openssh_private_key(blob + ret, end, priv);
240         } else
241                 ret = read_pem_private_key(path, priv);
242 free_blob:
243         free(blob);
244         return ret;
245 }
246
247 int apc_get_pubkey(const char *key_file, struct asymmetric_key **result)
248 {
249         unsigned char *blob;
250         size_t decoded_size;
251         int ret;
252         struct asymmetric_key *pub = alloc(sizeof(*pub));
253
254         ret = decode_public_key(key_file, &blob, &decoded_size);
255         if (ret < 0)
256                 goto out;
257         ret = read_public_key(blob + ret, decoded_size - ret, pub);
258         if (ret < 0)
259                 goto free_blob;
260         ret = RSA_size(pub->rsa);
261         assert(ret > 0);
262         *result = pub;
263 free_blob:
264         free(blob);
265 out:
266         if (ret < 0) {
267                 free(pub);
268                 *result = NULL;
269                 PARA_ERROR_LOG("can not load key %s\n", key_file);
270         }
271         return ret;
272 }
273
274 void apc_free_pubkey(struct asymmetric_key *pub)
275 {
276         if (!pub)
277                 return;
278         RSA_free(pub->rsa);
279         free(pub);
280 }
281
282 int apc_priv_decrypt(const char *key_file, unsigned char *outbuf,
283                 unsigned char *inbuf, int inlen)
284 {
285         struct asymmetric_key *priv;
286         int ret;
287
288         ret = check_private_key_file(key_file);
289         if (ret < 0)
290                 return ret;
291         if (inlen < 0)
292                 return -E_RSA;
293         priv = alloc(sizeof(*priv));
294         ret = get_private_key(key_file, priv);
295         if (ret < 0) {
296                 free(priv);
297                 return ret;
298         }
299         /*
300          * RSA is vulnerable to timing attacks. Generate a random blinding
301          * factor to protect against this kind of attack.
302          */
303         ret = -E_BLINDING;
304         if (RSA_blinding_on(priv->rsa, NULL) == 0)
305                 goto out;
306         ret = RSA_private_decrypt(inlen, inbuf, outbuf, priv->rsa,
307                 RSA_PKCS1_OAEP_PADDING);
308         RSA_blinding_off(priv->rsa);
309         if (ret <= 0)
310                 ret = -E_DECRYPT;
311 out:
312         RSA_free(priv->rsa);
313         free(priv);
314         return ret;
315 }
316
317 int apc_pub_encrypt(struct asymmetric_key *pub, unsigned char *inbuf,
318                 unsigned len, unsigned char *outbuf)
319 {
320         int ret, flen = len; /* RSA_public_encrypt expects a signed int */
321
322         if (flen < 0)
323                 return -E_ENCRYPT;
324         ret = RSA_public_encrypt(flen, inbuf, outbuf, pub->rsa,
325                 RSA_PKCS1_OAEP_PADDING);
326         return ret < 0? -E_ENCRYPT : ret;
327 }
328
329 struct stream_cipher {
330         EVP_CIPHER_CTX *aes;
331 };
332
333 struct stream_cipher *sc_new(const unsigned char *data, int len)
334 {
335         struct stream_cipher *sc = alloc(sizeof(*sc));
336
337         assert(len >= 2 * AES_CRT128_BLOCK_SIZE);
338         sc->aes = EVP_CIPHER_CTX_new();
339         EVP_EncryptInit_ex(sc->aes, EVP_aes_128_ctr(), NULL, data,
340                 data + AES_CRT128_BLOCK_SIZE);
341         return sc;
342 }
343
344 void sc_free(struct stream_cipher *sc)
345 {
346         if (!sc)
347                 return;
348         EVP_CIPHER_CTX_free(sc->aes);
349         free(sc);
350 }
351
352 static void aes_ctr128_crypt(EVP_CIPHER_CTX *ctx, struct iovec *src,
353                 struct iovec *dst)
354 {
355         int ret, inlen = src->iov_len, outlen, tmplen;
356
357         *dst = (typeof(*dst)) {
358                 /* Add one for the terminating zero byte. */
359                 .iov_base = alloc(inlen + 1),
360                 .iov_len = inlen
361         };
362         ret = EVP_EncryptUpdate(ctx, dst->iov_base, &outlen, src->iov_base, inlen);
363         assert(ret != 0);
364         ret = EVP_EncryptFinal_ex(ctx, dst->iov_base + outlen, &tmplen);
365         assert(ret != 0);
366         outlen += tmplen;
367         ((char *)dst->iov_base)[outlen] = '\0';
368         dst->iov_len = outlen;
369 }
370
371 void sc_crypt(struct stream_cipher *sc, struct iovec *src, struct iovec *dst)
372 {
373         return aes_ctr128_crypt(sc->aes, src, dst);
374 }
375
376 void hash_function(const char *data, unsigned long len, unsigned char *hash)
377 {
378         EVP_MD_CTX *c = EVP_MD_CTX_new();
379         int ret = EVP_DigestInit_ex(c, EVP_sha1(), NULL);
380         assert(ret != 0);
381         ret = EVP_DigestUpdate(c, data, len);
382         assert(ret != 0);
383         ret = EVP_DigestFinal_ex(c, hash, NULL);
384         assert(ret != 0);
385         EVP_MD_CTX_free(c);
386 }
387
388 void hash2_function(const char *data, unsigned long len, unsigned char *hash)
389 {
390         EVP_MD_CTX *c = EVP_MD_CTX_new();
391         int ret = EVP_DigestInit_ex(c, EVP_sha256(), NULL);
392         assert(ret != 0);
393         ret = EVP_DigestUpdate(c, data, len);
394         assert(ret != 0);
395         ret = EVP_DigestFinal_ex(c, hash, NULL);
396         assert(ret != 0);
397         EVP_MD_CTX_free(c);
398 }