Fri, 15 Nov 2024 21:50:20 +0100
add reference counting to download window
/* * 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 m = 16 - dec->ivpos; size_t cp = m > len ? len : m; 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; }