07c078cdadd810be615747d6280d934db9a46cdf
[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) {
76         free(n->ekey);
77         free(n->hkey);
78         free(n);
79     } else {
80         *skey = n;
81     }
82     return ret;
83 }
84
85 apr_status_t HMAC_BUFFER(struct seal_key *skey, struct databuf *buffer,
86                          struct databuf *result)
87 {
88     HMAC_CTX hmac_ctx = { 0 };
89     unsigned int len;
90     int ret;
91
92     /* now MAC the buffer */
93     HMAC_CTX_init(&hmac_ctx);
94
95     ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
96                        skey->cipher->key_len, skey->md, NULL);
97     if (ret == 0) goto done;
98
99     ret = HMAC_Update(&hmac_ctx, buffer->value, buffer->length);
100     if (ret == 0) goto done;
101
102     ret = HMAC_Final(&hmac_ctx, result->value, &len);
103
104 done:
105     HMAC_CTX_cleanup(&hmac_ctx);
106     if (ret == 0) return EFAULT;
107
108     result->length = len;
109     return 0;
110 }
111
112 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
113                          struct databuf *plain, struct databuf *cipher)
114 {
115     int blksz = skey->cipher->block_size;
116     apr_status_t err = EFAULT;
117     EVP_CIPHER_CTX ctx = { 0 };
118     uint8_t rbuf[blksz];
119     struct databuf hmacbuf;
120     int outlen, totlen;
121     int ret;
122
123     EVP_CIPHER_CTX_init(&ctx);
124
125     /* confounder to avoid exposing random numbers directly to clients
126      * as IVs */
127     ret = apr_generate_random_bytes(rbuf, sizeof(rbuf));
128     if (ret != 0) goto done;
129
130     if (cipher->length == 0) {
131         /* add space for confounder and padding and MAC */
132         cipher->length = (plain->length / blksz + 2) * blksz;
133         cipher->value = apr_palloc(p, cipher->length + skey->md->md_size);
134         if (!cipher->value) {
135             err = ENOMEM;
136             goto done;
137         }
138     }
139
140     ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
141     if (ret == 0) goto done;
142     totlen = 0;
143
144     outlen = cipher->length;
145     ret = EVP_EncryptUpdate(&ctx, cipher->value, &outlen, rbuf, sizeof(rbuf));
146     if (ret == 0) goto done;
147     totlen += outlen;
148
149     outlen = cipher->length - totlen;
150     ret = EVP_EncryptUpdate(&ctx, &cipher->value[totlen], &outlen,
151                             plain->value, plain->length);
152     if (ret == 0) goto done;
153     totlen += outlen;
154
155     outlen = cipher->length - totlen;
156     ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[totlen], &outlen);
157     if (ret == 0) goto done;
158     totlen += outlen;
159
160     /* now MAC the buffer */
161     cipher->length = totlen;
162     hmacbuf.value = &cipher->value[totlen];
163     ret = HMAC_BUFFER(skey, cipher, &hmacbuf);
164     if (ret != 0) goto done;
165
166     cipher->length += hmacbuf.length;
167     err = 0;
168
169 done:
170     EVP_CIPHER_CTX_cleanup(&ctx);
171     return err;
172 }
173
174 apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
175                            struct databuf *cipher, struct databuf *plain)
176 {
177     apr_status_t err = EFAULT;
178     EVP_CIPHER_CTX ctx = { 0 };
179     unsigned char mac[skey->md->md_size];
180     struct databuf hmacbuf;
181     int outlen, totlen;
182     volatile bool equal = true;
183     int ret, i;
184
185     /* check MAC first */
186     cipher->length -= skey->md->md_size;
187     hmacbuf.value = mac;
188     ret = HMAC_BUFFER(skey, cipher, &hmacbuf);
189     if (ret != 0) goto done;
190
191     if (hmacbuf.length != skey->md->md_size) goto done;
192     for (i = 0; i < skey->md->md_size; i++) {
193         if (cipher->value[cipher->length + i] != mac[i]) equal = false;
194         /* not breaking intentionally,
195          * or we would allow an oracle attack */
196     }
197     if (!equal) goto done;
198
199     EVP_CIPHER_CTX_init(&ctx);
200
201     if (plain->length == 0) {
202         plain->length = cipher->length;
203         plain->value = apr_palloc(p, plain->length);
204         if (!plain->value) {
205             err = ENOMEM;
206             goto done;
207         }
208     }
209
210     ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
211     if (ret == 0) goto done;
212
213     totlen = 0;
214     outlen = plain->length;
215     ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
216                             cipher->value, cipher->length);
217     if (ret == 0) goto done;
218
219     totlen += outlen;
220     outlen = plain->length - totlen;
221     ret = EVP_DecryptFinal_ex(&ctx, plain->value, &outlen);
222     if (ret == 0) goto done;
223
224     totlen += outlen;
225     /* now remove the confounder */
226     totlen -= skey->cipher->block_size;
227     memmove(plain->value, plain->value + skey->cipher->block_size, totlen);
228
229     plain->length = totlen;
230     err = 0;
231
232 done:
233     EVP_CIPHER_CTX_cleanup(&ctx);
234     return err;
235 }
236
237 int get_mac_size(struct seal_key *skey)
238 {
239     if (skey) {
240         return skey->md->md_size;
241     } else {
242         return 0;
243     }
244 }