Fix incorrect free() usage
[mod_auth_gssapi.git] / src / crypto.c
1 /* Copyright (C) 2014 mod_auth_gssapi authors - See COPYING for (C) terms */
2
3 #include <openssl/evp.h>
4 #include <openssl/hmac.h>
5 #include <openssl/rand.h>
6 #include <stdbool.h>
7 #include "crypto.h"
8
9 struct seal_key {
10     const EVP_CIPHER *cipher;
11     const EVP_MD *md;
12     unsigned char *ekey;
13     unsigned char *hkey;
14 };
15
16 apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
17                              struct databuf *keys)
18 {
19     struct seal_key *n;
20     int keylen;
21     int ret;
22
23     n = apr_pcalloc(p, sizeof(*n));
24     if (!n) return ENOMEM;
25
26     n->cipher = EVP_aes_128_cbc();
27     if (!n->cipher) {
28         ret = EFAULT;
29         goto done;
30     }
31
32     keylen = n->cipher->key_len;
33
34     n->md = EVP_sha256();
35     if (!n->md) {
36         ret = EFAULT;
37         goto done;
38     }
39
40     n->ekey = apr_palloc(p, keylen);
41     if (!n->ekey) {
42         ret = ENOMEM;
43         goto done;
44     }
45
46     n->hkey = apr_palloc(p, keylen);
47     if (!n->hkey) {
48         ret = ENOMEM;
49         goto done;
50     }
51
52     if (keys) {
53         if (keys->length != (keylen * 2)) {
54             ret = EINVAL;
55             goto done;
56         }
57         memcpy(n->ekey, keys->value, keylen);
58         memcpy(n->hkey, keys->value + keylen, keylen);
59     } else {
60         ret = apr_generate_random_bytes(n->ekey, keylen);
61         if (ret != 0) {
62             ret = EFAULT;
63             goto done;
64         }
65
66         ret = apr_generate_random_bytes(n->hkey, keylen);
67         if (ret != 0) {
68             ret = EFAULT;
69             goto done;
70         }
71     }
72
73     ret = 0;
74 done:
75     if (ret == 0) {
76         *skey = n;
77     }
78     return ret;
79 }
80
81 apr_status_t HMAC_BUFFER(struct seal_key *skey, struct databuf *buffer,
82                          struct databuf *result)
83 {
84     HMAC_CTX hmac_ctx = { 0 };
85     unsigned int len;
86     int ret;
87
88     /* now MAC the buffer */
89     HMAC_CTX_init(&hmac_ctx);
90
91     ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
92                        skey->cipher->key_len, skey->md, NULL);
93     if (ret == 0) goto done;
94
95     ret = HMAC_Update(&hmac_ctx, buffer->value, buffer->length);
96     if (ret == 0) goto done;
97
98     ret = HMAC_Final(&hmac_ctx, result->value, &len);
99
100 done:
101     HMAC_CTX_cleanup(&hmac_ctx);
102     if (ret == 0) return EFAULT;
103
104     result->length = len;
105     return 0;
106 }
107
108 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
109                          struct databuf *plain, struct databuf *cipher)
110 {
111     int blksz = skey->cipher->block_size;
112     apr_status_t err = EFAULT;
113     EVP_CIPHER_CTX ctx = { 0 };
114     uint8_t rbuf[blksz];
115     struct databuf hmacbuf;
116     int outlen, totlen;
117     int ret;
118
119     EVP_CIPHER_CTX_init(&ctx);
120
121     /* confounder to avoid exposing random numbers directly to clients
122      * as IVs */
123     ret = apr_generate_random_bytes(rbuf, sizeof(rbuf));
124     if (ret != 0) goto done;
125
126     if (cipher->length == 0) {
127         /* add space for confounder and padding and MAC */
128         cipher->length = (plain->length / blksz + 2) * blksz;
129         cipher->value = apr_palloc(p, cipher->length + skey->md->md_size);
130         if (!cipher->value) {
131             err = ENOMEM;
132             goto done;
133         }
134     }
135
136     ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
137     if (ret == 0) goto done;
138     totlen = 0;
139
140     outlen = cipher->length;
141     ret = EVP_EncryptUpdate(&ctx, cipher->value, &outlen, rbuf, sizeof(rbuf));
142     if (ret == 0) goto done;
143     totlen += outlen;
144
145     outlen = cipher->length - totlen;
146     ret = EVP_EncryptUpdate(&ctx, &cipher->value[totlen], &outlen,
147                             plain->value, plain->length);
148     if (ret == 0) goto done;
149     totlen += outlen;
150
151     outlen = cipher->length - totlen;
152     ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[totlen], &outlen);
153     if (ret == 0) goto done;
154     totlen += outlen;
155
156     /* now MAC the buffer */
157     cipher->length = totlen;
158     hmacbuf.value = &cipher->value[totlen];
159     ret = HMAC_BUFFER(skey, cipher, &hmacbuf);
160     if (ret != 0) goto done;
161
162     cipher->length += hmacbuf.length;
163     err = 0;
164
165 done:
166     EVP_CIPHER_CTX_cleanup(&ctx);
167     return err;
168 }
169
170 apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
171                            struct databuf *cipher, struct databuf *plain)
172 {
173     apr_status_t err = EFAULT;
174     EVP_CIPHER_CTX ctx = { 0 };
175     unsigned char mac[skey->md->md_size];
176     struct databuf hmacbuf;
177     int outlen, totlen;
178     volatile bool equal = true;
179     int ret, i;
180
181     /* check MAC first */
182     cipher->length -= skey->md->md_size;
183     hmacbuf.value = mac;
184     ret = HMAC_BUFFER(skey, cipher, &hmacbuf);
185     if (ret != 0) goto done;
186
187     if (hmacbuf.length != skey->md->md_size) goto done;
188     for (i = 0; i < skey->md->md_size; i++) {
189         if (cipher->value[cipher->length + i] != mac[i]) equal = false;
190         /* not breaking intentionally,
191          * or we would allow an oracle attack */
192     }
193     if (!equal) goto done;
194
195     EVP_CIPHER_CTX_init(&ctx);
196
197     if (plain->length == 0) {
198         plain->length = cipher->length;
199         plain->value = apr_palloc(p, plain->length);
200         if (!plain->value) {
201             err = ENOMEM;
202             goto done;
203         }
204     }
205
206     ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
207     if (ret == 0) goto done;
208
209     totlen = 0;
210     outlen = plain->length;
211     ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
212                             cipher->value, cipher->length);
213     if (ret == 0) goto done;
214
215     totlen += outlen;
216     outlen = plain->length - totlen;
217     ret = EVP_DecryptFinal_ex(&ctx, plain->value, &outlen);
218     if (ret == 0) goto done;
219
220     totlen += outlen;
221     /* now remove the confounder */
222     totlen -= skey->cipher->block_size;
223     memmove(plain->value, plain->value + skey->cipher->block_size, totlen);
224
225     plain->length = totlen;
226     err = 0;
227
228 done:
229     EVP_CIPHER_CTX_cleanup(&ctx);
230     return err;
231 }
232
233 int get_mac_size(struct seal_key *skey)
234 {
235     if (skey) {
236         return skey->md->md_size;
237     } else {
238         return 0;
239     }
240 }