libidav/crypto.c

Sun, 17 Dec 2023 14:25:34 +0100

author
Mike Becker <universe@uap-core.de>
date
Sun, 17 Dec 2023 14:25:34 +0100
changeset 797
edbb20b1438d
parent 791
38796c7e32b6
child 816
839fefbdedc7
permissions
-rw-r--r--

[Makefile] fix missing rules preventing dry-runs

We have to support dry-runs, because many IDEs are using
dry-runs to collect build information.

Some rules have dependencies that expect certain files or
directories to be just present. We added respective build
rules which invoke the test program. This way, the behavior
when running make normally is exactly the same, but dry-runs
are also not failing now.

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright 2018 Olaf Wintermann. All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions are met:
 *
 *   1. Redistributions of source code must retain the above copyright
 *      notice, this list of conditions and the following disclaimer.
 *
 *   2. Redistributions in binary form must reproduce the above copyright
 *      notice, this list of conditions and the following disclaimer in the
 *      documentation and/or other materials provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
 * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
 * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
 * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
 * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
 * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
 * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
 * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
 * POSSIBILITY OF SUCH DAMAGE.
 */

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>

#ifndef _WIN32
#include <unistd.h>
#endif

#include "utils.h"

#include "crypto.h"

/* -------------------- OpenSSL Crypto Functions -------------------- */
#ifdef DAV_USE_OPENSSL

#if OPENSSL_VERSION_NUMBER < 0x10000000L

static EVP_CIPHER_CTX* create_evp_cipher_ctx() {
    EVP_CIPHER_CTX *ctx = malloc(sizeof(EVP_CIPHER_CTX));
    EVP_CIPHER_CTX_init(ctx);
    return ctx;
}

static void free_evp_cipher_ctx(EVP_CIPHER_CTX *ctx) {
    EVP_CIPHER_CTX_cleanup(ctx);
    free(ctx);
}

#define EVP_CIPHER_CTX_new() create_evp_cipher_ctx()
#define EVP_CIPHER_CTX_free(ctx) free_evp_cipher_ctx(ctx)

#endif

int dav_rand_bytes(unsigned char *buf, size_t len) {
    return !RAND_bytes(buf, len);
}

AESDecrypter* aes_decrypter_new(DavKey *key, void *stream, dav_write_func write_func) {
    AESDecrypter *dec = calloc(1, sizeof(AESDecrypter));
    SHA256_Init(&dec->sha256);
    dec->stream = stream;
    dec->write = write_func;
    dec->key = key;
    dec->init = 0;
    dec->ivpos = 0;
    
    return dec;
}

void aes_decrypter_init(AESDecrypter *dec) {
    //EVP_CIPHER_CTX_init(&dec->ctx);
    dec->ctx = EVP_CIPHER_CTX_new();
    dec->init = 1;
    if(dec->key->type == DAV_KEY_AES128) {
        EVP_DecryptInit_ex(
                dec->ctx,
                EVP_aes_128_cbc(),
                NULL,
                dec->key->data,
                dec->ivtmp);
    } else if(dec->key->type == DAV_KEY_AES256) {
        EVP_DecryptInit_ex(
                dec->ctx,
                EVP_aes_256_cbc(),
                NULL,
                dec->key->data,
                dec->ivtmp);
    } else {
        fprintf(stderr, "unknown key type\n");
        exit(-1);
    }
}

size_t aes_write(const void *buf, size_t s, size_t n, AESDecrypter *dec) {
    int len = s*n;
    if(!dec->init) {
        size_t n = 16 - dec->ivpos;
        size_t cp = n > len ? len : n;
        memcpy(dec->ivtmp + dec->ivpos, buf, cp);
        dec->ivpos += cp;
        if(dec->ivpos >= 16) {
            aes_decrypter_init(dec);
        }
        if(len == cp) {
            return len;
        } else {
            buf = (char*)buf + cp;
            len -= cp;
        }
    }
    
    int outlen = len + 16;
    unsigned char *out = malloc(outlen);
    EVP_DecryptUpdate(dec->ctx, out, &outlen, buf, len);
    ssize_t wlen = dec->write(out, 1, outlen, dec->stream);
    SHA256_Update(&dec->sha256, out, wlen);
    free(out);
    return (s*n) / s;
}

void aes_decrypter_shutdown(AESDecrypter *dec) {
    if(dec->init) {
        void *out = malloc(128);
        int len = 0;
        EVP_DecryptFinal_ex(dec->ctx, out, &len);
        dec->write(out, 1, len, dec->stream);
        SHA256_Update(&dec->sha256, out, len);
        free(out);
        //EVP_CIPHER_CTX_cleanup(&dec->ctx);
        EVP_CIPHER_CTX_free(dec->ctx);
    }
}

void aes_decrypter_close(AESDecrypter *dec) {
    free(dec);
}


AESEncrypter* aes_encrypter_new(DavKey *key, void *stream, dav_read_func read_func, dav_seek_func seek_func) {
    unsigned char *iv = malloc(16);
    if(!RAND_bytes(iv, 16)) {
        free(iv);
        return NULL;
    }
    
    AESEncrypter *enc = malloc(sizeof(AESEncrypter));
    SHA256_Init(&enc->sha256);
    enc->stream = stream;
    enc->read = read_func;
    enc->seek = seek_func;
    enc->tmp = NULL;
    enc->tmplen = 0;
    enc->tmpoff = 0;
    enc->end = 0;
    enc->iv = iv;
    enc->ivlen = 16;
    
    //EVP_CIPHER_CTX_init(&enc->ctx);
    enc->ctx = EVP_CIPHER_CTX_new();
    if(key->type == DAV_KEY_AES128) {
        EVP_EncryptInit_ex(enc->ctx, EVP_aes_128_cbc(), NULL, key->data, enc->iv);
    } else if(key->type == DAV_KEY_AES256) {
        EVP_EncryptInit_ex(enc->ctx, EVP_aes_256_cbc(), NULL, key->data, enc->iv);
    } else {
        fprintf(stderr, "unknown key type\n");
        exit(-1);
    }
    return enc;
}

size_t aes_read(void *buf, size_t s, size_t n, AESEncrypter *enc) {
    size_t len = s*n;
    if(enc->tmp) {
        size_t tmp_diff = enc->tmplen - enc->tmpoff;
        size_t cp_len = tmp_diff > len ? len : tmp_diff;
        memcpy(buf, enc->tmp + enc->tmpoff, cp_len);
        enc->tmpoff += cp_len;
        if(enc->tmpoff >= enc->tmplen) {
            free(enc->tmp);
            enc->tmp = NULL;
            enc->tmplen = 0;
            enc->tmpoff = 0;
        }
        return cp_len / s;
    }
    
    if(enc->end) {
        return 0;
    }
    
    void *in = malloc(len);
    size_t in_len = enc->read(in, 1, len, enc->stream);
    
    SHA256_Update(&enc->sha256, in, in_len);
    
    unsigned char *out = NULL;
    int outlen = 0;
    size_t ivl = enc->ivlen;
    if(in_len != 0) {
        outlen = len + 32;
        out = malloc(outlen + ivl);
        if(ivl > 0) {
            memcpy(out, enc->iv, ivl);
        }
        EVP_EncryptUpdate(enc->ctx, out + ivl, &outlen, in, in_len);
        // I think we don't need this
        /*
        if(in_len != len) {
            int newoutlen = 16;
            EVP_EncryptFinal_ex(enc->ctx, out + ivl + outlen, &newoutlen);
            outlen += newoutlen;
            enc->end = 1;
        }
        */
    } else {
        out = malloc(16);
        EVP_EncryptFinal_ex(enc->ctx, out, &outlen);
        enc->end = 1;
    }
    enc->tmp = (char*)out;
    enc->tmplen = outlen + ivl;
    enc->tmpoff = 0;
    
    if(enc->ivlen > 0) {
        enc->ivlen = 0;
    }
    
    free(in);
    
    return aes_read(buf, s, n, enc);
}

void aes_encrypter_close(AESEncrypter *enc) {
    if(enc->tmp) {
        free(enc->tmp);
    }
    if(enc->iv) {
        free(enc->iv);
    }
    //EVP_CIPHER_CTX_cleanup(&enc->ctx);
    EVP_CIPHER_CTX_free(enc->ctx);
    free(enc);
}

int aes_encrypter_reset(AESEncrypter  *enc, curl_off_t offset, int origin) {
    if(origin != SEEK_SET || offset != 0 || !enc->seek) {
        return CURL_SEEKFUNC_CANTSEEK;
    }
    
    enc->ivlen = 16;
    if(enc->seek(enc->stream, 0, SEEK_SET) != 0) {
        return CURL_SEEKFUNC_FAIL;
    }
    return CURL_SEEKFUNC_OK;
}


char* aes_encrypt(const char *in, size_t len, DavKey *key) {
    unsigned char iv[16];
    if(!RAND_bytes(iv, 16)) {
        return NULL;
    }
    
    //EVP_CIPHER_CTX ctx;
    //EVP_CIPHER_CTX_init(&ctx);
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if(key->type == DAV_KEY_AES128) {
        EVP_EncryptInit_ex(
                ctx,
                EVP_aes_128_cbc(),
                NULL,
                (unsigned char*)key->data,
                iv);
    } else if(key->type == DAV_KEY_AES256) {
        EVP_EncryptInit_ex(
                ctx,
                EVP_aes_256_cbc(),
                NULL,
                (unsigned char*)key->data,
                iv);
    } else {
        //EVP_CIPHER_CTX_cleanup(&ctx);
        EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }
    
    //int len = strlen(in);
    int buflen = len + 64;
    unsigned char *buf = calloc(1, buflen);
    memcpy(buf, iv, 16);
    
    int l = buflen - 16;
    EVP_EncryptUpdate(ctx, buf + 16, &l, (unsigned char*)in, len);
    
    int f = 0;
    EVP_EncryptFinal_ex(ctx, buf + 16 + l, &f);
    char *out = util_base64encode((char*)buf, 16 + l + f);
    free(buf);
    EVP_CIPHER_CTX_free(ctx);
    //EVP_CIPHER_CTX_cleanup(&ctx);
    
    return out;
}

char* aes_decrypt(const char *in, size_t *length, DavKey *key) {
    int len;
    unsigned char *buf = (unsigned char*)util_base64decode_len(in, &len);
    
    //EVP_CIPHER_CTX ctx;
    //EVP_CIPHER_CTX_init(&ctx);
    EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
    if(key->type == DAV_KEY_AES128) {
        EVP_DecryptInit_ex(
                ctx,
                EVP_aes_128_cbc(),
                NULL,
                key->data,
                buf);
    } else if(key->type == DAV_KEY_AES256) {
        EVP_DecryptInit_ex(
                ctx,
                EVP_aes_256_cbc(),
                NULL,
                key->data,
                buf);
    } else {
        //EVP_CIPHER_CTX_cleanup(&ctx);
        EVP_CIPHER_CTX_free(ctx);
        return NULL;
    }
    
    unsigned char *out = malloc(len + 1);
    int outlen = len;
    unsigned char *in_buf = buf + 16;
    int inlen = len - 16;
    int f = 0; 
    
    EVP_DecryptUpdate(ctx, out, &outlen, in_buf, inlen);
    EVP_DecryptFinal_ex(ctx, out + outlen, &f);
    out[outlen + f] = '\0';
    free(buf);
    //EVP_CIPHER_CTX_cleanup(&ctx);
    EVP_CIPHER_CTX_free(ctx);
    
    *length = outlen + f;
    return (char*)out;
}


void dav_get_hash(DAV_SHA_CTX *sha256, unsigned char *buf){
    SHA256_Final((unsigned char*)buf, sha256);
}

char* dav_create_hash(const char *data, size_t len) {
    unsigned char hash[DAV_SHA256_DIGEST_LENGTH];
    DAV_SHA_CTX ctx;
    SHA256_Init(&ctx);
    SHA256_Update(&ctx, data, len);
    SHA256_Final(hash, &ctx);
    return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);
}

DAV_SHA_CTX* dav_hash_init(void) {
    DAV_SHA_CTX *ctx = malloc(sizeof(DAV_SHA_CTX));
    SHA256_Init(ctx);
    return ctx;
}

void dav_hash_update(DAV_SHA_CTX *ctx, const char *data, size_t len) {
    SHA256_Update(ctx, data, len);
}

void dav_hash_final(DAV_SHA_CTX *ctx, unsigned char *buf) {
    SHA256_Final(buf, ctx);
    free(ctx);
}

#if OPENSSL_VERSION_NUMBER < 0x10100000L
static int crypto_pw2key_error = 0;
DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) {
    if(!crypto_pw2key_error) {
        fprintf(stderr, "Error: password key derivation not supported on this platform: openssl to old\n");
        crypto_pw2key_error = 1;
    }
    return 0;
}

#else
DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) {
    if(!password) {
        return NULL;
    }
    size_t len = strlen(password);
    if(len == 0) {
        return NULL;
    }
    
    // setup key data and length
    unsigned char keydata[32];
    int keylen = 32;
    switch(enc) {
        case DAV_KEY_AES128: keylen = 16; break;
        case DAV_KEY_AES256: keylen = 32; break;
        default: return NULL;
    }
    
    // generate key
    switch(pwfunc) {
        case DAV_PWFUNC_PBKDF2_SHA256: {
            PKCS5_PBKDF2_HMAC(
                    password,
                    len,
                    salt,
                    saltlen,
                    DAV_CRYPTO_ITERATION_COUNT,
                    EVP_sha256(),
                    keylen,
                    keydata);
            break;
        }
        case DAV_PWFUNC_PBKDF2_SHA512: {
            PKCS5_PBKDF2_HMAC(
                    password,
                    len,
                    salt,
                    saltlen,
                    DAV_CRYPTO_ITERATION_COUNT,
                    EVP_sha512(),
                    keylen,
                    keydata);
            break;
        }
        default: return NULL;
    }
    
    // create DavKey with generated data
    DavKey *key = malloc(sizeof(DavKey));
    key->data = malloc(keylen);
    key->length = keylen;
    key->name = NULL;
    key->type = enc;
    memcpy(key->data, keydata, keylen);
    return key;
}
#endif

#endif


/* -------------------- Apple Crypto Functions -------------------- */
#ifdef DAV_CRYPTO_COMMON_CRYPTO

#define RANDOM_BUFFER_LENGTH 256
static char randbuf[RANDOM_BUFFER_LENGTH];
static int rbufpos = RANDOM_BUFFER_LENGTH;

int dav_rand_bytes(unsigned char *buf, size_t len) {
    if(len + rbufpos > RANDOM_BUFFER_LENGTH) {
        int devr = open("/dev/urandom", O_RDONLY);
        if(devr == -1) {
            return 1;
        }
        
        if(read(devr, randbuf, RANDOM_BUFFER_LENGTH) < RANDOM_BUFFER_LENGTH) {
            close(devr);
            return 1;
        }
        
        rbufpos = 0;
        if(len > RANDOM_BUFFER_LENGTH) {
            int err = 0;
            if(read(devr, buf, len) < len) {
                err = 1;
            }
            close(devr);
            return err;
        }
        
        close(devr);
    }
    
    char *r = randbuf;
    memcpy(buf, r + rbufpos, len);
    rbufpos += len;
    
    return 0;
}

AESDecrypter* aes_decrypter_new(DavKey *key, void *stream, dav_write_func write_func) {
    AESDecrypter *dec = calloc(1, sizeof(AESDecrypter));
    CC_SHA256_Init(&dec->sha256);
    dec->stream = stream;
    dec->write = write_func;
    dec->key = key;
    dec->init = 0;
    dec->ivpos = 0;
    
    return dec;
}


void aes_decrypter_init(AESDecrypter *dec) {
    //EVP_CIPHER_CTX_init(&dec->ctx);
    dec->init = 1;
    
    CCCryptorRef cryptor;
    CCCryptorStatus status;
    if(dec->key->type == DAV_KEY_AES128) {
        status = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, dec->key->data, dec->key->length, dec->ivtmp, &cryptor);
    } else if(dec->key->type == DAV_KEY_AES256) {
        status = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, dec->key->data, dec->key->length, dec->ivtmp, &cryptor);
    } else {
        fprintf(stderr, "unknown key type\n");
        exit(-1);
    }
    dec->ctx = cryptor;
}

size_t aes_write(const void *buf, size_t s, size_t n, AESDecrypter *dec) {
    int len = s*n;
    if(!dec->init) {
        size_t n = 16 - dec->ivpos;
        size_t cp = n > len ? len : n;
        memcpy(dec->ivtmp + dec->ivpos, buf, cp);
        dec->ivpos += cp;
        if(dec->ivpos >= 16) {
            aes_decrypter_init(dec);
        }
        if(len == cp) {
            return len;
        } else {
            buf = (char*)buf + cp;
            len -= cp;
        }
    }
    
    int outlen = len + 16;
    unsigned char *out = malloc(outlen);
    
    CCCryptorStatus status;
    size_t avail = outlen;
    size_t moved = 0;
    status = CCCryptorUpdate(dec->ctx, buf, len, out, avail, &moved);
    
    ssize_t wlen = dec->write(out, 1, moved, dec->stream);
    CC_SHA256_Update(&dec->sha256, out, wlen);
    free(out);
    return (s*n) / s;
}

void aes_decrypter_shutdown(AESDecrypter *dec) {
    if(dec->init) {
        void *out = malloc(128);
        size_t len = 0;
        //EVP_DecryptFinal_ex(dec->ctx, out, &len);
        CCCryptorFinal(dec->ctx, out, 128, &len);
        
        
        dec->write(out, 1, len, dec->stream);
        CC_SHA256_Update(&dec->sha256, out, len);
        free(out);
        //EVP_CIPHER_CTX_cleanup(&dec->ctx);
        //EVP_CIPHER_CTX_free(dec->ctx);
    }
}

void aes_decrypter_close(AESDecrypter *dec) {
    
}

AESEncrypter* aes_encrypter_new(DavKey *key, void *stream, dav_read_func read_func, dav_seek_func seek_func) {
    unsigned char *iv = malloc(16);
    if(dav_rand_bytes(iv, 16)) {
        return NULL;
    }
    
    CCCryptorRef cryptor;
    CCCryptorStatus status;
    if(key->type == DAV_KEY_AES128) {
        status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, key->data, key->length, iv, &cryptor);
    } else if(key->type == DAV_KEY_AES256) {
        status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, key->data, key->length, iv, &cryptor);
    } else {
        free(iv);
        return NULL;
    }
    
    AESEncrypter *enc = malloc(sizeof(AESEncrypter));
    enc->ctx = cryptor;
    CC_SHA256_Init(&enc->sha256);
    enc->stream = stream;
    enc->read = read_func;
    enc->seek = seek_func;
    enc->tmp = NULL;
    enc->tmplen = 0;
    enc->tmpoff = 0;
    enc->end = 0;
    enc->iv = iv;
    enc->ivlen = 16;
    
    return enc;
}

size_t aes_read(void *buf, size_t s, size_t n, AESEncrypter *enc) {
    size_t len = s*n;
    if(enc->tmp) {
        size_t tmp_diff = enc->tmplen - enc->tmpoff;
        size_t cp_len = tmp_diff > len ? len : tmp_diff;
        memcpy(buf, enc->tmp + enc->tmpoff, cp_len);
        enc->tmpoff += cp_len;
        if(enc->tmpoff >= enc->tmplen) {
            free(enc->tmp);
            enc->tmp = NULL;
            enc->tmplen = 0;
            enc->tmpoff = 0;
        }
        return cp_len / s;
    }
    
    if(enc->end) {
        return 0;
    }
    
    void *in = malloc(len);
    size_t in_len = enc->read(in, 1, len, enc->stream);
    
    CC_SHA256_Update(&enc->sha256, in, in_len);
    
    unsigned char *out = NULL;
    size_t outlen = 0;
    size_t ivl = enc->ivlen;
    if(in_len != 0) {
        outlen = len + 32;
        out = malloc(outlen + ivl);
        if(ivl > 0) {
            memcpy(out, enc->iv, ivl);
        }
        
        CCCryptorStatus status;
        size_t avail = outlen;
        status = CCCryptorUpdate(enc->ctx, in, in_len, out + ivl, avail, &outlen);
        // TODO: check if this still works
        /*
        if(in_len != len) {
            size_t newoutlen = 16;
            status = CCCryptorFinal(enc->ctx, out + ivl + outlen, 16, &newoutlen);
            outlen += newoutlen;
            enc->end = 1;
        }
        */
    } else {
        out = malloc(32);
        CCCryptorStatus status;
        size_t avail = outlen;
        status = CCCryptorFinal(enc->ctx, out, 32, &outlen);
        enc->end = 1;
    }
    enc->tmp = (char*)out;
    enc->tmplen = outlen + ivl;
    enc->tmpoff = 0;
    
    if(enc->ivlen > 0) {
        enc->ivlen = 0;
    }
    
    free(in);
    
    return aes_read(buf, s, n, enc);
}

int aes_encrypter_reset(AESEncrypter  *enc, curl_off_t offset, int origin) {
    if(origin != SEEK_SET || offset != 0 || !enc->seek) {
        return CURL_SEEKFUNC_CANTSEEK;
    }
    
    enc->ivlen = 16;
    if(enc->seek(enc->stream, 0, SEEK_SET) != 0) {
        return CURL_SEEKFUNC_FAIL;
    }
    return CURL_SEEKFUNC_OK;
}

void aes_encrypter_close(AESEncrypter *enc) {
    if(enc->tmp) {
        free(enc->tmp);
    }
    if(enc->iv) {
        free(enc->iv);
    }
    // TODO: cleanup cryptor
    free(enc);
}

char* aes_encrypt(const char *in, size_t len, DavKey *key) {
    unsigned char iv[16];
    if(dav_rand_bytes(iv, 16)) {
        return NULL;
    }
    
    CCCryptorRef cryptor;
    CCCryptorStatus status;
    if(key->type == DAV_KEY_AES128) {
        status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, key->data, key->length, iv, &cryptor);
    } else if(key->type == DAV_KEY_AES256) {
        status = CCCryptorCreate(kCCEncrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, key->data, key->length, iv, &cryptor);
    } else {
        return NULL;
    }
    
    if(status != kCCSuccess) {
        return NULL;
    }
    
    int buflen = len + 64;
    char *buf = calloc(1, buflen);
    memcpy(buf, iv, 16);
    
    int pos = 16;
    size_t avail = buflen - 16;
    size_t moved;
    char *out = buf + 16;
    
    status = CCCryptorUpdate(cryptor, in,
         len, out, avail,
         &moved);
    if(status != kCCSuccess) {
        free(buf);
        return NULL;
    }
    
    pos += moved;
    avail -= moved;
    out += moved;
    
    status = CCCryptorFinal(cryptor, out, avail, &moved);
    if(status != kCCSuccess) {
        free(buf);
        return NULL;
    }
    
    pos += moved;
    
    char *b64enc = util_base64encode(buf, pos);
    free(buf);
    
    return b64enc;
}

char* aes_decrypt(const char *in, size_t *len, DavKey *key) {
    int inlen;
    unsigned char *buf = (unsigned char*)util_base64decode_len(in, &inlen);
    
    CCCryptorRef cryptor;
    CCCryptorStatus status;
    if(key->type == DAV_KEY_AES128) {
        status = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES128, kCCOptionPKCS7Padding, key->data, key->length, buf, &cryptor);
    } else if(key->type == DAV_KEY_AES256) {
        status = CCCryptorCreate(kCCDecrypt, kCCAlgorithmAES, kCCOptionPKCS7Padding, key->data, key->length, buf, &cryptor);
    } else {
        free(buf);
        return NULL;
    }
    
    if(status != kCCSuccess) {
        free(buf);
        return NULL;
    }
    
    char *out = malloc(inlen + 1);
    size_t outavail = inlen;
    size_t outlen = 0;
    
    unsigned char *inbuf = buf + 16;
    inlen -= 16;
    
    size_t moved = 0;
    status = CCCryptorUpdate(cryptor, inbuf, inlen, out, outavail, &moved);
    if(status != kCCSuccess) {
        free(buf);
        free(out);
        // TODO cryptor
        return NULL;
    }
    
    outlen += moved;
    outavail -= moved;
    
    status = CCCryptorFinal(cryptor, out + outlen, outavail, &moved);
    if(status != kCCSuccess) {
        free(buf);
        free(out);
        // TODO cryptor
        return NULL;
    }
    
    outlen += moved;
    out[outlen] = 0;
    
    *len = outlen;
    return out;
}

void dav_get_hash(DAV_SHA_CTX *sha256, unsigned char *buf) {
    CC_SHA256_Final(buf, sha256);
}

char* dav_create_hash(const char *data, size_t len) {
    unsigned char hash[DAV_SHA256_DIGEST_LENGTH];
    CC_SHA256((const unsigned char*)data, len, hash);
    return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);
}

DAV_SHA_CTX* dav_hash_init(void) {
    DAV_SHA_CTX *ctx = malloc(sizeof(DAV_SHA_CTX));
    CC_SHA256_Init(ctx);
    return ctx;
}

void dav_hash_update(DAV_SHA_CTX *ctx, const char *data, size_t len) {
    CC_SHA256_Update(ctx, data, len);
}

void dav_hash_final(DAV_SHA_CTX *ctx, unsigned char *buf) {
    CC_SHA256_Final(buf, ctx);
    free(ctx);
}

DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) {
    if(!password) {
        return NULL;
    }
    size_t len = strlen(password);
    if(len == 0) {
        return NULL;
    }
    
    // setup key data and length
    unsigned char keydata[32];
    int keylen = 32;
    switch(enc) {
        case DAV_KEY_AES128: keylen = 16; break;
        case DAV_KEY_AES256: keylen = 32; break;
        default: return NULL;
    }
    
    // generate key
    switch(pwfunc) {
        case DAV_PWFUNC_PBKDF2_SHA256: {
            int result = CCKeyDerivationPBKDF(
                    kCCPBKDF2,
                    password,
                    len,
                    salt,
                    saltlen,
                    kCCPRFHmacAlgSHA256,
                    DAV_CRYPTO_ITERATION_COUNT,
                    keydata,
                    keylen);
            if(result) {
                return NULL;
            }
            break;
        }
        case DAV_PWFUNC_PBKDF2_SHA512: {
            int result = CCKeyDerivationPBKDF(
                    kCCPBKDF2,
                    password,
                    len,
                    salt,
                    saltlen,
                    kCCPRFHmacAlgSHA512,
                    DAV_CRYPTO_ITERATION_COUNT,
                    keydata,
                    keylen);
            if(result) {
                return NULL;
            }
            break;
        }
        default: return NULL;
    }
    
    // create DavKey with generated data
    DavKey *key = malloc(sizeof(DavKey));
    key->data = malloc(keylen);
    key->length = keylen;
    key->name = NULL;
    key->type = enc;
    memcpy(key->data, keydata, keylen);
    return key;
}

#endif

/* -------------------- Windows Crypto Functions -------------------- */
#ifdef DAV_CRYPTO_CNG

static void cng_cleanup(BCRYPT_ALG_HANDLE hAesAlg, BCRYPT_KEY_HANDLE hKey, BCRYPT_HASH_HANDLE hHash, void *pbObject) {
    if(hAesAlg) {
        BCryptCloseAlgorithmProvider(hAesAlg,0);
    }
    if(hKey) {
        BCryptDestroyKey(hKey);
    }
    if(hHash) {
        BCryptDestroyHash(hHash);
    }
    if(pbObject) {
        free(pbObject);
    }
}

static int cng_init_key(BCRYPT_ALG_HANDLE *alg, BCRYPT_KEY_HANDLE *key, void **keyobj, DavKey *aesKey) {
    BCRYPT_ALG_HANDLE hAesAlg = NULL;
    BCRYPT_KEY_HANDLE hKey    = NULL;
    
    void *pbKeyObject     = NULL;
    ULONG keyObjectLength = 0;
    
    ULONG result = 0;
    
    // check DavKey and get AES key length
    if(!aesKey) {
        return 1;
    }
    
    ULONG aesKeyLength = 0;
    if(aesKey->type == DAV_KEY_AES128) {
        aesKeyLength = 16;
    } else if(aesKey->type == DAV_KEY_AES256) {
        aesKeyLength = 32;
    }
    if(aesKeyLength > aesKey->length || !aesKey->data) {
        // invalid DavKey
        return 1;
    }
    
    // initialize BCrypt stuff
    if(BCryptOpenAlgorithmProvider(&hAesAlg, BCRYPT_AES_ALGORITHM, NULL, 0)) {
        fprintf(stderr, "Error: BCryptOpenAlgorithmProvider failed\n");
        return 1;
    }
    
    if(BCryptGetProperty(hAesAlg, BCRYPT_OBJECT_LENGTH, (PUCHAR)&keyObjectLength, sizeof(DWORD), &result, 0)) {
        fprintf(stderr, "Error: BCrypt: Cannot get BCRYPT_OBJECT_LENGTH\n");
        cng_cleanup(hAesAlg, hKey, NULL, pbKeyObject);
        return 1;
    }
    
    if(BCryptSetProperty(hAesAlg, BCRYPT_CHAINING_MODE, (PBYTE)BCRYPT_CHAIN_MODE_CBC, sizeof(BCRYPT_CHAIN_MODE_CBC), 0)) {
        fprintf(stderr, "Error: BCrypt: Cannot set CBC mode\n");
        cng_cleanup(hAesAlg, hKey, NULL, pbKeyObject);
        return 1;
    }
    
    pbKeyObject = calloc(1, keyObjectLength);
    if(!pbKeyObject) {
        cng_cleanup(hAesAlg, hKey, NULL, pbKeyObject);
        return 1;
    }
    
    // init key
    if(BCryptGenerateSymmetricKey(hAesAlg, &hKey, pbKeyObject, keyObjectLength, aesKey->data, aesKeyLength, 0)) {
        fprintf(stderr, "Error: BCrypt: Cannot set key\n");
        cng_cleanup(hAesAlg, hKey, NULL, pbKeyObject);
        return 1;
    }
    
    *alg = hAesAlg;
    *key = hKey;
    *keyobj = pbKeyObject;
    
    return 0;
}

static int cng_hash_init(WinBCryptSHACTX *ctx) {
    if(BCryptOpenAlgorithmProvider(&ctx->hAlg, BCRYPT_SHA256_ALGORITHM, NULL, 0)) {
        fprintf(stderr, "Error: BCryptOpenAlgorithmProvider failed\n");
        return 1;
    }
    
    ULONG hashObjectLen;
    ULONG result;
    if(BCryptGetProperty(ctx->hAlg, BCRYPT_OBJECT_LENGTH, (PBYTE)&hashObjectLen, sizeof(DWORD), &result, 0)) {
        cng_cleanup(ctx->hAlg, NULL, NULL, NULL);
        return 1;
    }
    
    ctx->pbHashObject = calloc(1, hashObjectLen);
    
    if(BCryptCreateHash(ctx->hAlg, &ctx->hHash, ctx->pbHashObject, hashObjectLen, NULL, 0, 0)) {
        cng_cleanup(ctx->hAlg, NULL, ctx->hHash, ctx->pbHashObject);
        return 1;
    }
    
    return 0;
}


int dav_rand_bytes(unsigned char *buf, size_t len) {
    if(BCryptGenRandom(NULL, (unsigned char*)buf, (ULONG)len, BCRYPT_USE_SYSTEM_PREFERRED_RNG)) {
        return 1;
    }
    return 0;
}

AESDecrypter* aes_decrypter_new(DavKey *key, void *stream, dav_write_func write_func) {
    AESDecrypter *dec = calloc(1, sizeof(AESDecrypter));
    if(!dec) {
        return NULL;
    }
    if(cng_hash_init(&dec->sha256)) {
        free(dec);
        return NULL;
    }
    
    dec->stream = stream;
    dec->write = write_func;
    dec->key = key;
    dec->init = 0;
    dec->ivpos = 0;
      
    return dec;
}

static void aes_decrypter_init(AESDecrypter *dec) {
    if(cng_init_key(&dec->ctx.hAlg, &dec->ctx.hKey, &dec->ctx.pbKeyObject, dec->key)) {
        fprintf(stderr, "Error: cng_init_key failed\n");
        exit(-1);
    }
    // copy iv
    memcpy(dec->ctx.pbIV, dec->ivtmp, 16);
}

size_t aes_write(const void *buf, size_t s, size_t n, AESDecrypter *dec) {
    int len = s*n;
    if(!dec->init) {
        dec->init = 1;
        
        size_t n = 16 - dec->ivpos;
        size_t cp = n > len ? len : n;
        memcpy(dec->ivtmp + dec->ivpos, buf, cp);
        dec->ivpos += cp;
        if(dec->ivpos >= 16) {
            aes_decrypter_init(dec);
        }
        if(len == cp) {
            return len;
        } else {
            buf = (char*)buf + cp;
            len -= cp;
        }
    }
    
    // the cipher text must be a multiply of 16
    // remaining bytes are stored in ctx.buf and must be added to cibuf
    // the next time
    size_t cbufalloc = len + 64;
    ULONG clen = 0;
    char *cbuf = malloc(cbufalloc);
    
    // add previous remaining bytes
    if(dec->ctx.buflen > 0) {
        memcpy(cbuf, dec->ctx.buf, dec->ctx.buflen);
        clen = dec->ctx.buflen;
    }
    // add current bytes
    memcpy(cbuf + clen, buf, len);
    clen += len;
    
    // check if the message fits the blocksize
    int remaining = clen % 16;
    if(remaining == 0) {
        // decrypt last block next time, or in aes_decrypter_shutdown
        // this makes sure, that shutdown always decrypts the last block
        // with BCRYPT_BLOCK_PADDING flag
        remaining = 16;
    }
    
    // add remaining bytes to ctx.buf for the next aes_write run
    clen -= remaining;
    memcpy(dec->ctx.buf, cbuf + clen, remaining);
    dec->ctx.buflen = remaining;
    
    // ready to decrypt the message
    ULONG outlen = clen + 32;
       
    // decrypt
    if(clen > 0) {
        unsigned char* out = malloc(outlen);

        ULONG enc_len = 0;
        ULONG status = BCryptDecrypt(dec->ctx.hKey, cbuf, clen, NULL, dec->ctx.pbIV, 16, out, outlen, &enc_len, 0);
        if(status > 0) {
            fprintf(stderr, "Error: BCryptDecrypt failed: 0x%X\n", status);
            free(out);
            free(cbuf);
            return 0;
        }      
        outlen = enc_len;

        // write decrypted data to the output stream and update the hash
        dec->write(out, 1, outlen, dec->stream);
        BCryptHashData(dec->sha256.hHash, out, outlen, 0);

        free(out);
    }
    
    free(cbuf);
    
    return (s*n) / s;
}

void aes_decrypter_shutdown(AESDecrypter *dec) {
    if(dec->init && dec->ctx.buflen > 0) { 
        ULONG outlen = 64;
        char out[64];
        if(BCryptDecrypt(dec->ctx.hKey, dec->ctx.buf, dec->ctx.buflen, NULL, dec->ctx.pbIV, 16, out, outlen, &outlen, BCRYPT_BLOCK_PADDING)) {
            fprintf(stderr, "Error: BCryptDecrypt failed\n");
            return;
        }
        dec->write(out, 1, outlen, dec->stream);
        BCryptHashData(dec->sha256.hHash, out, outlen, 0);
    }
}

void aes_decrypter_close(AESDecrypter *dec) {
    cng_cleanup(dec->ctx.hAlg, dec->ctx.hKey, NULL, dec->ctx.pbKeyObject);
    cng_cleanup(dec->sha256.hAlg, NULL, dec->sha256.hHash, dec->sha256.pbHashObject);
    free(dec);
}

AESEncrypter* aes_encrypter_new(DavKey *key, void *stream, dav_read_func read_func, dav_seek_func seek_func) {
    unsigned char *iv = malloc(16);
    if(dav_rand_bytes(iv, 16)) {
        free(iv);
        return NULL;
    }
    
    AESEncrypter *enc = calloc(1, sizeof(AESEncrypter));
    if(cng_hash_init(&enc->sha256)) {
        free(iv);
        free(enc);
        return NULL;
    }
    
    enc->stream = stream;
    enc->read = read_func;
    enc->seek = seek_func;
    enc->tmp = NULL;
    enc->tmplen = 0;
    enc->tmpoff = 0;
    enc->end = 0;
    enc->iv = iv;
    enc->ivlen = 0;
    
    if(cng_init_key(&enc->ctx.hAlg, &enc->ctx.hKey, &enc->ctx.pbKeyObject, key)) {
        fprintf(stderr, "Error: cng_init_key failed\n");
        exit(-1);
    }
    
    enc->ctx.buflen = 0;
    memcpy(enc->ctx.pbIV, iv, 16);
    
    return enc;
}

size_t aes_read(void *buf, size_t s, size_t n, AESEncrypter *enc) {
    size_t len = s*n;
    size_t nread = 0; 
    
    if(enc->tmp) {
        // the temp buffer contains bytes that are already encrypted, but
        // the last aes_read had not enough read buffer space
        
        // in case we have a tmp buf, we just return this 
        size_t tmp_diff = enc->tmplen - enc->tmpoff;
        size_t cp_len = tmp_diff > len ? len : tmp_diff;
        memcpy(buf, enc->tmp + enc->tmpoff, cp_len);
        enc->tmpoff += cp_len;
        if(enc->tmpoff >= enc->tmplen) {
            free(enc->tmp);
            enc->tmp = NULL;
            enc->tmplen = 0;
            enc->tmpoff = 0;
        }
        return cp_len / s;
    }
    
    if(enc->ivlen < 16) {
        size_t copy_iv_len = 16 - enc->ivlen;
        copy_iv_len = len > copy_iv_len ? copy_iv_len : len;
        
        memcpy(buf, enc->iv, copy_iv_len);
        (char*)buf += copy_iv_len;
        len -= copy_iv_len;
        nread = copy_iv_len;
        
        enc->ivlen += copy_iv_len;
        
        if(len == 0) {
            return copy_iv_len / s;
        }
    }
    
    if(enc->end) {
        return 0;
    }
    
    size_t remaining = len % 16;
    len -= remaining;
    
    if(len > 256) {
        len -= 16; // optimization for avoiding tmp buffer usage
    }
    
    size_t inalloc = len;
    ULONG  inlen = 0;
    unsigned char *in = malloc(inalloc);
    
    // fill the input buffer
    while(inlen < inalloc) {
        size_t r = enc->read(in + inlen, 1, inalloc - inlen, enc->stream);
        if(r == 0) {
            enc->end = 1;
            break;
        }
        inlen += r;
    }
    
    if(inlen == 0) {
        return nread / s;
    }
    
    // hash read data
    BCryptHashData(enc->sha256.hHash, in, inlen, 0);
    
    // create output buffer
    ULONG outalloc = inlen + 16;
    ULONG outlen = 0;
    char *out = malloc(outalloc);
    
    // encrypt
    int flags = 0;
    if(inlen % 16 != 0) {
        enc->end = 1;
    }
    if(enc->end) {
        flags = BCRYPT_BLOCK_PADDING;
    }
    if(BCryptEncrypt(enc->ctx.hKey, in, inlen, NULL, enc->ctx.pbIV, 16, out, outalloc, &outlen, flags)) {
        fprintf(stderr, "Error: BCryptEncrypt failed\n");
    }
    
    // check if the output fits in buf, if not, save the remaining bytes in tmp
    if(outlen > len) {
        size_t tmplen = outlen - len;
        char *tmp = malloc(tmplen);
        memcpy(tmp, out+len, tmplen);
        
        enc->tmp = tmp;
        enc->tmplen = tmplen;
        enc->tmpoff = 0;
        
        outlen = len;
    }
    
    // fill read buffer and return
    memcpy(buf, out, outlen);
    nread += outlen;
    
    free(in);
    free(out);
    
    return nread / s;
}

void aes_encrypter_close(AESEncrypter *enc) {
    enc->end = 1;
}

int aes_encrypter_reset(AESEncrypter  *enc, curl_off_t offset, int origin) {
    if(origin != SEEK_SET || offset != 0 || !enc->seek) {
        return CURL_SEEKFUNC_CANTSEEK;
    }
    
    enc->ivlen = 0;
    memcpy(enc->ctx.pbIV, enc->iv, 16);
    if(enc->seek(enc->stream, 0, SEEK_SET) != 0) {
        return CURL_SEEKFUNC_FAIL;
    }
    return CURL_SEEKFUNC_OK;
}

char* aes_encrypt(const char *in, size_t len, DavKey *key) {
    // create random IV
    char iv[16];
    if(dav_rand_bytes(iv, 16)) {
        return NULL;
    }
    
    // initialize bcrypt stuff
    BCRYPT_ALG_HANDLE hAlg = NULL;
    BCRYPT_KEY_HANDLE hKey = NULL;
    void *pbKeyObject = NULL;
    if(cng_init_key(&hAlg, &hKey, &pbKeyObject, key)) {
        return NULL;
    }
    
    // create output buffer
    ULONG outlen = len + 128;
    char *out = malloc(outlen);
    
    // the output must start with the IV
    memcpy(out, iv, 16);
    char *encbuf = out + 16;
    ULONG enclen = outlen - 16;
    ULONG encoutlen = 0;
    
    // encrypt
    if(BCryptEncrypt(hKey, (PUCHAR)in, len, NULL, (PUCHAR)iv, 16, encbuf, enclen, &encoutlen, BCRYPT_BLOCK_PADDING)) {
        fprintf(stderr, "Error: BCryptEncrypt failed\n");
        cng_cleanup(hAlg, hKey, NULL, pbKeyObject);
        free(out);
        return NULL;
    }
    
    outlen = encoutlen + 16; // length of encrypted data + 16 bytes IV
    
    // base64 encode
    char *outstr = util_base64encode(out, outlen);
    
    cng_cleanup(hAlg, hKey, NULL, pbKeyObject);
    free(out);
    
    return outstr;
}

char* aes_decrypt(const char *in, size_t *len, DavKey *key) {
    BCRYPT_ALG_HANDLE hAlg = NULL;
    BCRYPT_KEY_HANDLE hKey = NULL;
    void *pbKeyObject = NULL;
    if(cng_init_key(&hAlg, &hKey, &pbKeyObject, key)) {
        return NULL;
    }
    
    int inlen;
    unsigned char *buf = (unsigned char*)util_base64decode_len(in, &inlen);
    if(inlen < 16 || !buf) {
        cng_cleanup(hAlg, hKey, NULL, pbKeyObject);
        if(buf) {
            free(buf);
        }
        return NULL;
    }
    
    // encrypted data starts with IV
    char iv[16];
    memcpy(iv, buf, 16);
    
    // decrypt data
    char *data = buf + 16; // encrypted data starts after IV
    size_t datalen = inlen - 16;
    
    // create output buffer
    ULONG outlen = inlen;
    char *out = malloc(outlen + 1);
    
    // decrypt
    if(BCryptDecrypt(hKey, data, datalen, NULL, iv, 16, out, outlen, &outlen, BCRYPT_BLOCK_PADDING)) {
        cng_cleanup(hAlg, hKey, NULL, pbKeyObject);
        free(out);
        free(buf);
        return NULL;
    }
    
    // decrypt finished, return
    out[outlen] = 0;
    *len = (size_t)outlen;
    return out;
}

void dav_get_hash(DAV_SHA_CTX *sha256, unsigned char *buf) {
    BCryptFinishHash(sha256->hHash, buf, DAV_SHA256_DIGEST_LENGTH, 0);
}


char* dav_create_hash(const char *data, size_t len) {
    unsigned char hash[DAV_SHA256_DIGEST_LENGTH];
    DAV_SHA_CTX *ctx = dav_hash_init();
    if(ctx) {
        dav_hash_update(ctx, data, len);
        dav_hash_final(ctx, hash);
    }
    return util_hexstr(hash, DAV_SHA256_DIGEST_LENGTH);
}

DAV_SHA_CTX* dav_hash_init(void) {
    DAV_SHA_CTX *ctx = malloc(sizeof(DAV_SHA_CTX));
    if(!ctx) {
        return NULL;
    }
    if(cng_hash_init(ctx)) {
        free(ctx);
        return NULL;
    }
    return ctx;
}

void dav_hash_update(DAV_SHA_CTX *ctx, const char *data, size_t len) {
    BCryptHashData(ctx->hHash, (PUCHAR)data, len, 0);
}

void dav_hash_final(DAV_SHA_CTX *ctx, unsigned char *buf) {
    BCryptFinishHash(ctx->hHash, (PUCHAR)buf, DAV_SHA256_DIGEST_LENGTH, 0);
    
    // cleanup
    cng_cleanup(ctx->hAlg, NULL, ctx->hHash, ctx->pbHashObject);
    free(ctx);
}

DavKey* dav_pw2key(const char *password, const unsigned char *salt, int saltlen, int pwfunc, int enc) {
    if(!password) {
        return NULL;
    }
    size_t len = strlen(password);
    if(len == 0) {
        return NULL;
    }
    
    // setup key data and length
    unsigned char keydata[128];
    int keylen = 32;
    switch(enc) {
        case DAV_KEY_AES128: keylen = 16; break;
        case DAV_KEY_AES256: keylen = 32; break;
        default: return NULL;
    }
    
    LPCWSTR algid;
    switch(pwfunc) {
        case DAV_PWFUNC_PBKDF2_SHA256: algid = BCRYPT_SHA256_ALGORITHM; break;
        case DAV_PWFUNC_PBKDF2_SHA512: algid = BCRYPT_SHA512_ALGORITHM; break;
        default: return NULL;
    }
    
    // open algorithm provider
    BCRYPT_ALG_HANDLE hAlg;
    ULONG status = BCryptOpenAlgorithmProvider(&hAlg, algid, NULL, BCRYPT_ALG_HANDLE_HMAC_FLAG);
    if(status > 0) {
        fprintf(stderr, "Error: dav_pw2key: BCryptOpenAlgorithmProvider failed: 0x%X\n", (unsigned int)status);
        return NULL;
    }
    
    // derive key
    status =  BCryptDeriveKeyPBKDF2(
            hAlg,
            (PUCHAR)password,
            len,
            (PUCHAR)salt,
            saltlen,
            DAV_CRYPTO_ITERATION_COUNT,
            keydata,
            128,
            0);
    
    BCryptCloseAlgorithmProvider(hAlg,0);
    
    if(status) {
        fprintf(stderr, "Error: dav_pw2key: BCryptDeriveKeyPBKDF2 failed: 0x%X\n", (unsigned int)status);
        return NULL;
    }
    
    // create DavKey with generated data
    DavKey *key = malloc(sizeof(DavKey));
    key->data = malloc(keylen);
    key->length = keylen;
    key->name = NULL;
    key->type = enc;
    memcpy(key->data, keydata, keylen);
    return key;
}
#endif



CxBuffer* aes_encrypt_buffer(CxBuffer *in, DavKey *key) {
    CxBuffer *encbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
    if(!encbuf) {
        return NULL;
    }
    
    AESEncrypter *enc = aes_encrypter_new(
            key,
            in,
            (dav_read_func)cxBufferRead,
            NULL);
    if(!enc) {
        cxBufferFree(encbuf);
        return NULL;
    }
    
    char buf[1024];
    size_t r;
    while((r = aes_read(buf, 1, 1024, enc)) > 0) {
        cxBufferWrite(buf, 1, r, encbuf);
    }
    aes_encrypter_close(enc);
    
    encbuf->pos = 0;
    return encbuf;
}

CxBuffer* aes_decrypt_buffer(CxBuffer *in, DavKey *key) {
    CxBuffer *decbuf = cxBufferCreate(NULL, in->size, cxDefaultAllocator, CX_BUFFER_FREE_CONTENTS|CX_BUFFER_AUTO_EXTEND);
    if(!decbuf) {
        return NULL;
    }
    AESDecrypter *dec = aes_decrypter_new(
            key,
            decbuf,
            (dav_write_func)cxBufferWrite);
    if(!dec) {
        cxBufferFree(decbuf);
        return NULL;
    }
    
    aes_write(in->space, 1, in->size, dec);
    aes_decrypter_shutdown(dec);
    aes_decrypter_close(dec);
    decbuf->pos = 0;
    return decbuf;
}

mercurial