Add mod_session support
[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(struct seal_key **skey)
17 {
18     struct seal_key *n;
19     int ret;
20
21     n = calloc(1, sizeof(*n));
22     if (!n) return ENOMEM;
23
24     n->cipher = EVP_aes_128_cbc();
25     if (!n->cipher) {
26         free(n);
27         return EFAULT;
28     }
29
30     n->md = EVP_sha256();
31     if (!n->md) {
32         free(n);
33         return EFAULT;
34     }
35
36     n->ekey = malloc(n->cipher->key_len);
37     if (!n->ekey) {
38         free(n);
39         return ENOMEM;
40     }
41
42     n->hkey = malloc(n->cipher->key_len);
43     if (!n->hkey) {
44         free(n);
45         return ENOMEM;
46     }
47
48     ret = RAND_bytes(n->ekey, n->cipher->key_len);
49     if (ret == 0) {
50         free(n->ekey);
51         free(n->hkey);
52         free(n);
53         return EFAULT;
54     }
55
56     ret = RAND_bytes(n->hkey, n->cipher->key_len);
57     if (ret == 0) {
58         free(n->ekey);
59         free(n->hkey);
60         free(n);
61         return EFAULT;
62     }
63
64     *skey = n;
65     return 0;
66 }
67
68 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
69                          struct databuf *plain, struct databuf *cipher)
70 {
71     apr_status_t err = EFAULT;
72     EVP_CIPHER_CTX ctx = { 0 };
73     HMAC_CTX hmac_ctx = { 0 };
74     uint8_t rbuf[16];
75     unsigned int len;
76     int outlen, totlen;
77     int ret;
78
79     EVP_CIPHER_CTX_init(&ctx);
80
81     /* confounder to avoid exposing random numbers directly to clients
82      * as IVs */
83     ret = RAND_bytes(rbuf, 16);
84     if (ret == 0) goto done;
85
86     if (cipher->length == 0) {
87         /* add space for confounder and padding and MAC */
88         cipher->length = (plain->length / 16 + 2) * 16;
89         cipher->value = apr_palloc(p, cipher->length + skey->md->md_size);
90         if (!cipher->value) {
91             err = ENOMEM;
92             goto done;
93         }
94     }
95
96     ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
97     if (ret == 0) goto done;
98     totlen = 0;
99
100     outlen = cipher->length;
101     ret = EVP_EncryptUpdate(&ctx, cipher->value, &outlen, rbuf, 16);
102     if (ret == 0) goto done;
103     totlen += outlen;
104
105     outlen = cipher->length - totlen;
106     ret = EVP_EncryptUpdate(&ctx, &cipher->value[totlen], &outlen,
107                             plain->value, plain->length);
108     if (ret == 0) goto done;
109     totlen += outlen;
110
111     outlen = cipher->length - totlen;
112     ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[totlen], &outlen);
113     if (ret == 0) goto done;
114     totlen += outlen;
115
116     /* now MAC the buffer */
117     HMAC_CTX_init(&hmac_ctx);
118
119     ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
120                        skey->cipher->key_len, skey->md, NULL);
121     if (ret == 0) goto done;
122
123     ret = HMAC_Update(&hmac_ctx, cipher->value, totlen);
124     if (ret == 0) goto done;
125
126     ret = HMAC_Final(&hmac_ctx, &cipher->value[totlen], &len);
127     if (ret == 0) goto done;
128
129     cipher->length = totlen + len;
130     err = 0;
131
132 done:
133     EVP_CIPHER_CTX_cleanup(&ctx);
134     HMAC_CTX_cleanup(&hmac_ctx);
135     return err;
136 }
137
138 apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
139                            struct databuf *cipher, struct databuf *plain)
140 {
141     apr_status_t err = EFAULT;
142     EVP_CIPHER_CTX ctx = { 0 };
143     HMAC_CTX hmac_ctx = { 0 };
144     unsigned char mac[skey->md->md_size];
145     unsigned int len;
146     int outlen, totlen;
147     volatile bool equal = true;
148     int ret, i;
149
150     /* check MAC first */
151     HMAC_CTX_init(&hmac_ctx);
152
153     ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
154                        skey->cipher->key_len, skey->md, NULL);
155     if (ret == 0) goto done;
156
157     cipher->length -= skey->md->md_size;
158
159     ret = HMAC_Update(&hmac_ctx, cipher->value, cipher->length);
160     if (ret == 0) goto done;
161
162     ret = HMAC_Final(&hmac_ctx, mac, &len);
163     if (ret == 0) goto done;
164
165     if (len != skey->md->md_size) goto done;
166     for (i = 0; i < skey->md->md_size; i++) {
167         if (cipher->value[cipher->length + i] != mac[i]) equal = false;
168         /* not breaking intentionally,
169          * or we would allow an oracle attack */
170     }
171     if (!equal) goto done;
172
173     EVP_CIPHER_CTX_init(&ctx);
174
175     if (plain->length == 0) {
176         plain->length = cipher->length;
177         plain->value = apr_palloc(p, plain->length);
178         if (!plain->value) {
179             err = ENOMEM;
180             goto done;
181         }
182     }
183
184     ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
185     if (ret == 0) goto done;
186
187     totlen = 0;
188     outlen = plain->length;
189     ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
190                             cipher->value, cipher->length);
191     if (ret == 0) goto done;
192
193     totlen += outlen;
194     outlen = plain->length - totlen;
195     ret = EVP_DecryptFinal_ex(&ctx, plain->value, &outlen);
196     if (ret == 0) goto done;
197
198     totlen += outlen;
199     /* now remove the confounder */
200     totlen -= 16;
201     memmove(plain->value, plain->value + 16, totlen);
202
203     plain->length = totlen;
204     err = 0;
205
206 done:
207     EVP_CIPHER_CTX_cleanup(&ctx);
208     HMAC_CTX_cleanup(&hmac_ctx);
209     return err;
210 }