libidav/crypto.c

changeset 1
b5bb7b3cd597
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/libidav/crypto.c	Mon Jan 22 17:27:47 2024 +0100
@@ -0,0 +1,1542 @@
+/*
+ * 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