#include <stdbool.h>
#include "crypto.h"
-#define TAGSIZE 16
-
struct seal_key {
const EVP_CIPHER *cipher;
+ const EVP_MD *md;
unsigned char *ekey;
+ unsigned char *hkey;
};
apr_status_t SEAL_KEY_CREATE(apr_pool_t *p, struct seal_key **skey,
- struct databuf *key)
+ struct databuf *keys)
{
struct seal_key *n;
int keylen;
n = apr_pcalloc(p, sizeof(*n));
if (!n) return ENOMEM;
- n->cipher = EVP_aes_256_gcm();
+ n->cipher = EVP_aes_128_cbc();
if (!n->cipher) {
ret = EFAULT;
goto done;
keylen = n->cipher->key_len;
+ n->md = EVP_sha256();
+ if (!n->md) {
+ ret = EFAULT;
+ goto done;
+ }
+
n->ekey = apr_palloc(p, keylen);
if (!n->ekey) {
ret = ENOMEM;
goto done;
}
- if (key) {
- if (key->length < keylen) {
+ n->hkey = apr_palloc(p, keylen);
+ if (!n->hkey) {
+ ret = ENOMEM;
+ goto done;
+ }
+
+ if (keys) {
+ if (keys->length != (keylen * 2)) {
ret = EINVAL;
goto done;
}
- memcpy(n->ekey, key->value, keylen);
+ memcpy(n->ekey, keys->value, keylen);
+ memcpy(n->hkey, keys->value + keylen, keylen);
} else {
ret = apr_generate_random_bytes(n->ekey, keylen);
if (ret != 0) {
ret = EFAULT;
goto done;
}
+
+ ret = apr_generate_random_bytes(n->hkey, keylen);
+ if (ret != 0) {
+ ret = EFAULT;
+ goto done;
+ }
}
ret = 0;
done:
- if (ret) {
- free(n->ekey);
- free(n);
- } else {
+ if (ret == 0) {
*skey = n;
}
return ret;
}
+apr_status_t HMAC_BUFFER(struct seal_key *skey, struct databuf *buffer,
+ struct databuf *result)
+{
+ HMAC_CTX hmac_ctx = { 0 };
+ unsigned int len;
+ int ret;
+
+ /* now MAC the buffer */
+ HMAC_CTX_init(&hmac_ctx);
+
+ ret = HMAC_Init_ex(&hmac_ctx, skey->hkey,
+ skey->cipher->key_len, skey->md, NULL);
+ if (ret == 0) goto done;
+
+ ret = HMAC_Update(&hmac_ctx, buffer->value, buffer->length);
+ if (ret == 0) goto done;
+
+ ret = HMAC_Final(&hmac_ctx, result->value, &len);
+
+done:
+ HMAC_CTX_cleanup(&hmac_ctx);
+ if (ret == 0) return EFAULT;
+
+ result->length = len;
+ return 0;
+}
+
apr_status_t SEAL_BUFFER(apr_pool_t *p, struct seal_key *skey,
struct databuf *plain, struct databuf *cipher)
{
+ int blksz = skey->cipher->block_size;
apr_status_t err = EFAULT;
- EVP_CIPHER_CTX ctx = {};
- int minlen;
- int outlen;
+ EVP_CIPHER_CTX ctx = { 0 };
+ uint8_t rbuf[blksz];
+ struct databuf hmacbuf;
+ int outlen, totlen;
int ret;
EVP_CIPHER_CTX_init(&ctx);
- /* Add space for padding, IV and tag. */
- minlen = plain->length / skey->cipher->block_size + 1;
- minlen *= skey->cipher->block_size;
- minlen += skey->cipher->iv_len + TAGSIZE;
- if (cipher->length < minlen) {
- cipher->length = minlen;
- cipher->value = apr_palloc(p, cipher->length);
+ /* confounder to avoid exposing random numbers directly to clients
+ * as IVs */
+ ret = apr_generate_random_bytes(rbuf, sizeof(rbuf));
+ if (ret != 0) goto done;
+
+ if (cipher->length == 0) {
+ /* add space for confounder and padding and MAC */
+ cipher->length = (plain->length / blksz + 2) * blksz;
+ cipher->value = apr_palloc(p, cipher->length + skey->md->md_size);
if (!cipher->value) {
err = ENOMEM;
goto done;
}
}
- /* Generate IV. */
- ret = apr_generate_random_bytes(cipher->value, skey->cipher->iv_len);
+ ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
+ if (ret == 0) goto done;
+ totlen = 0;
+
+ outlen = cipher->length;
+ ret = EVP_EncryptUpdate(&ctx, cipher->value, &outlen, rbuf, sizeof(rbuf));
+ if (ret == 0) goto done;
+ totlen += outlen;
+
+ outlen = cipher->length - totlen;
+ ret = EVP_EncryptUpdate(&ctx, &cipher->value[totlen], &outlen,
+ plain->value, plain->length);
+ if (ret == 0) goto done;
+ totlen += outlen;
+
+ outlen = cipher->length - totlen;
+ ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[totlen], &outlen);
+ if (ret == 0) goto done;
+ totlen += outlen;
+
+ /* now MAC the buffer */
+ cipher->length = totlen;
+ hmacbuf.value = &cipher->value[totlen];
+ ret = HMAC_BUFFER(skey, cipher, &hmacbuf);
if (ret != 0) goto done;
- cipher->length = skey->cipher->iv_len;
-
- ret = EVP_EncryptInit_ex(&ctx, skey->cipher, NULL,
- skey->ekey, cipher->value);
- if (ret != 1) goto done;
-
- /* Encrypt the data. */
- outlen = 0;
- ret = EVP_EncryptUpdate(&ctx, &cipher->value[cipher->length],
- &outlen, plain->value, plain->length);
- if (ret != 1) goto done;
- cipher->length += outlen;
-
- outlen = 0;
- ret = EVP_EncryptFinal_ex(&ctx, &cipher->value[cipher->length], &outlen);
- if (ret != 1) goto done;
- cipher->length += outlen;
-
- /* Get the tag */
- ret = EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_GET_TAG, TAGSIZE,
- &cipher->value[cipher->length]);
- if (ret != 1) goto done;
- cipher->length += TAGSIZE;
+ cipher->length += hmacbuf.length;
err = 0;
done:
struct databuf *cipher, struct databuf *plain)
{
apr_status_t err = EFAULT;
- EVP_CIPHER_CTX ctx = {};
- int outlen;
- int ret;
+ EVP_CIPHER_CTX ctx = { 0 };
+ unsigned char mac[skey->md->md_size];
+ struct databuf hmacbuf;
+ int outlen, totlen;
+ volatile bool equal = true;
+ int ret, i;
+
+ /* check MAC first */
+ cipher->length -= skey->md->md_size;
+ hmacbuf.value = mac;
+ ret = HMAC_BUFFER(skey, cipher, &hmacbuf);
+ if (ret != 0) goto done;
+
+ if (hmacbuf.length != skey->md->md_size) goto done;
+ for (i = 0; i < skey->md->md_size; i++) {
+ if (cipher->value[cipher->length + i] != mac[i]) equal = false;
+ /* not breaking intentionally,
+ * or we would allow an oracle attack */
+ }
+ if (!equal) goto done;
EVP_CIPHER_CTX_init(&ctx);
- if (plain->length < cipher->length - skey->cipher->iv_len - TAGSIZE) {
- plain->length = cipher->length - skey->cipher->iv_len - TAGSIZE;
+ if (plain->length == 0) {
+ plain->length = cipher->length;
plain->value = apr_palloc(p, plain->length);
if (!plain->value) {
err = ENOMEM;
}
}
- ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL,
- skey->ekey, cipher->value);
- if (ret != 1) goto done;
- plain->length = 0;
+ ret = EVP_DecryptInit_ex(&ctx, skey->cipher, NULL, skey->ekey, NULL);
+ if (ret == 0) goto done;
- outlen = 0;
+ totlen = 0;
+ outlen = plain->length;
ret = EVP_DecryptUpdate(&ctx, plain->value, &outlen,
- &cipher->value[skey->cipher->iv_len],
- cipher->length - skey->cipher->iv_len - TAGSIZE);
- if (ret != 1) goto done;
- plain->length += outlen;
+ cipher->value, cipher->length);
+ if (ret == 0) goto done;
- ret = EVP_CIPHER_CTX_ctrl(&ctx, EVP_CTRL_GCM_SET_TAG, TAGSIZE,
- &cipher->value[cipher->length - TAGSIZE]);
- if (ret != 1) goto done;
+ totlen += outlen;
+ outlen = plain->length - totlen;
+ ret = EVP_DecryptFinal_ex(&ctx, plain->value, &outlen);
+ if (ret == 0) goto done;
- outlen = 0;
- ret = EVP_DecryptFinal_ex(&ctx, &plain->value[plain->length], &outlen);
- if (ret != 1) goto done;
- plain->length += outlen;
+ totlen += outlen;
+ /* now remove the confounder */
+ totlen -= skey->cipher->block_size;
+ memmove(plain->value, plain->value + skey->cipher->block_size, totlen);
+ plain->length = totlen;
err = 0;
done:
EVP_CIPHER_CTX_cleanup(&ctx);
return err;
}
+
+int get_mac_size(struct seal_key *skey)
+{
+ if (skey) {
+ return skey->md->md_size;
+ } else {
+ return 0;
+ }
+}