diff -r 2483f517c562 -r b5bb7b3cd597 libidav/crypto.c --- /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 +#include +#include +#include + +#ifndef _WIN32 +#include +#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; +}