Use aes-256-gcm rather than aes-128-cbc
[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 #define TAGSIZE 16
10
11 struct seal_key {
12     const EVP_CIPHER *cipher;
13     unsigned char *ekey;
14 };
15
16 apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
17                              struct databuf *key)
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_256_gcm();
27     if (!n->cipher) {
28         ret = EFAULT;
29         goto done;
30     }
31
32     keylen = n->cipher->key_len;
33
34     n->ekey = apr_palloc(p, keylen);
35     if (!n->ekey) {
36         ret = ENOMEM;
37         goto done;
38     }
39
40     if (key) {
41         if (key->length < keylen) {
42             ret = EINVAL;
43             goto done;
44         }
45         memcpy(n->ekey, key->value, keylen);
46     } else {
47         ret = apr_generate_random_bytes(n->ekey, keylen);
48         if (ret != 0) {
49             ret = EFAULT;
50             goto done;
51         }
52     }
53
54     ret = 0;
55 done:
56     if (ret) {
57         free(n->ekey);
58         free(n);
59     } else {
60         *skey = n;
61     }
62     return ret;
63 }
64
65 apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
66                          struct databuf *plain, struct databuf *cipher)
67 {
68     apr_status_t err = EFAULT;
69     EVP_CIPHER_CTX ctx = {};
70     int minlen;
71     int outlen;
72     int ret;
73
74     EVP_CIPHER_CTX_init(&ctx);
75
76         /* Add space for padding, IV and tag. */
77     minlen = plain->length / skey->cipher->block_size + 1;
78     minlen *= skey->cipher->block_size;
79     minlen += skey->cipher->iv_len + TAGSIZE;
80     if (cipher->length < minlen) {
81         cipher->length = minlen;
82         cipher->value = apr_palloc(p, cipher->length);
83         if (!cipher->value) {
84             err = ENOMEM;
85             goto done;
86         }
87     }
88
89     /* Generate IV. */
90     ret = apr_generate_random_bytes(cipher->value, skey->cipher->iv_len);
91     if (ret != 0) goto done;
92     cipher->length = skey->cipher->iv_len;
93
94     ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL,
95                              skey->ekey, cipher->value);
96     if (ret != 1) goto done;
97
98     /* Encrypt the data. */
99     outlen = 0;
100     ret = EVP_EncryptUpdate(&ctx, &cipher->value[cipher->length],
101                             &outlen, plain->value, plain->length);
102     if (ret != 1) goto done;
103     cipher->length += outlen;
104
105     outlen = 0;
106     ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[cipher->length], &outlen);
107     if (ret != 1) goto done;
108     cipher->length += outlen;
109
110     /* Get the tag */
111     ret = EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, TAGSIZE,
112                               &cipher->value[cipher->length]);
113     if (ret != 1) goto done;
114     cipher->length += TAGSIZE;
115
116     err = 0;
117
118 done:
119     EVP_CIPHER_CTX_cleanup(&ctx);
120     return err;
121 }
122
123 apr_status_t UNSEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
124                            struct databuf *cipher, struct databuf *plain)
125 {
126     apr_status_t err = EFAULT;
127     EVP_CIPHER_CTX ctx = {};
128     int outlen;
129     int ret;
130
131     EVP_CIPHER_CTX_init(&ctx);
132
133     if (plain->length < cipher->length - skey->cipher->iv_len - TAGSIZE) {
134         plain->length = cipher->length - skey->cipher->iv_len - TAGSIZE;
135         plain->value = apr_palloc(p, plain->length);
136         if (!plain->value) {
137             err = ENOMEM;
138             goto done;
139         }
140     }
141
142     ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL,
143                              skey->ekey, cipher->value);
144     if (ret != 1) goto done;
145     plain->length = 0;
146
147     outlen = 0;
148     ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
149                             &cipher->value[skey->cipher->iv_len],
150                             cipher->length - skey->cipher->iv_len - TAGSIZE);
151     if (ret != 1) goto done;
152     plain->length += outlen;
153
154     ret = EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, TAGSIZE,
155                               &cipher->value[cipher->length - TAGSIZE]);
156     if (ret != 1) goto done;
157
158     outlen = 0;
159     ret = EVP_DecryptFinal_ex(&ctx, &plain->value[plain->length], &outlen);
160     if (ret != 1) goto done;
161     plain->length += outlen;
162
163     err = 0;
164
165 done:
166     EVP_CIPHER_CTX_cleanup(&ctx);
167     return err;
168 }