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