Sun, 17 Dec 2023 15:33:50 +0100
fix faulty string to int conversion utilities
Probably it was expected that errno is set to EINVAL when illegal characters are encountered. But this is not standard and does not happen on every system, allowing illegal strings to be parsed as valid integers.
/* * 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; }