static int sqlcipher_ltc_activate(void *ctx) { unsigned char random_buffer[FORTUNA_MAX_SZ]; #ifndef SQLCIPHER_LTC_NO_MUTEX_RAND if(ltc_rand_mutex == NULL){ ltc_rand_mutex = sqlite3_mutex_alloc(SQLITE_MUTEX_FAST); } sqlite3_mutex_enter(ltc_rand_mutex); #endif sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ); if(ltc_init == 0) { if(register_prng(&fortuna_desc) < 0) return SQLITE_ERROR; if(register_cipher(&rijndael_desc) < 0) return SQLITE_ERROR; if(register_hash(&sha512_desc) < 0) return SQLITE_ERROR; if(register_hash(&sha256_desc) < 0) return SQLITE_ERROR; if(register_hash(&sha1_desc) < 0) return SQLITE_ERROR; if(fortuna_start(&prng) != CRYPT_OK) { return SQLITE_ERROR; } ltc_init = 1; } ltc_ref_count++; #ifndef SQLCIPHER_TEST sqlite3_randomness(FORTUNA_MAX_SZ, random_buffer); #endif #ifndef SQLCIPHER_LTC_NO_MUTEX_RAND sqlite3_mutex_leave(ltc_rand_mutex); #endif if(sqlcipher_ltc_add_random(ctx, random_buffer, FORTUNA_MAX_SZ) != SQLITE_OK) { return SQLITE_ERROR; } sqlcipher_memset(random_buffer, 0, FORTUNA_MAX_SZ); return SQLITE_OK; }
/** * allocate memory. Uses sqlite's internall malloc wrapper so memory can be * reference counted and leak detection works. Unless compiled with OMIT_MEMLOCK * attempts to lock the memory pages so sensitive information won't be swapped */ void* sqlcipher_malloc(int sz) { void *ptr = sqlite3Malloc(sz); sqlcipher_memset(ptr, 0, sz); #ifndef OMIT_MEMLOCK if(ptr) { #if defined(__unix__) || defined(__APPLE__) mlock(ptr, sz); #elif defined(_WIN32) #if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP)) VirtualLock(ptr, sz); #endif #endif } #endif return ptr; }
/** * Free and wipe memory. Uses SQLites internal sqlite3_free so that memory * can be countend and memory leak detection works in the test suite. * If ptr is not null memory will be freed. * If sz is greater than zero, the memory will be overwritten with zero before it is freed * If sz is > 0, and not compiled with OMIT_MEMLOCK, system will attempt to unlock the * memory segment so it can be paged */ void sqlcipher_free(void *ptr, int sz) { if(ptr) { if(sz > 0) { sqlcipher_memset(ptr, 0, sz); #ifndef OMIT_MEMLOCK #if defined(__unix__) || defined(__APPLE__) munlock(ptr, sz); #elif defined(_WIN32) #if !(defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_PHONE_APP || WINAPI_FAMILY == WINAPI_FAMILY_APP)) VirtualUnlock(ptr, sz); #endif #endif #endif } sqlite3_free(ptr); } }
static int sqlcipher_ltc_deactivate(void *ctx) { #ifndef SQLCIPHER_LTC_NO_MUTEX_RAND sqlite3_mutex_enter(ltc_rand_mutex); #endif ltc_ref_count--; if(ltc_ref_count == 0){ fortuna_done(&prng); sqlcipher_memset((void *)&prng, 0, sizeof(prng)); #ifndef SQLCIPHER_LTC_NO_MUTEX_RAND sqlite3_mutex_leave(ltc_rand_mutex); sqlite3_mutex_free(ltc_rand_mutex); ltc_rand_mutex = NULL; #endif } #ifndef SQLCIPHER_LTC_NO_MUTEX_RAND else { sqlite3_mutex_leave(ltc_rand_mutex); } #endif return SQLITE_OK; }
/* * ctx - codec context * pgno - page number in database * size - size in bytes of input and output buffers * mode - 1 to encrypt, 0 to decrypt * in - pointer to input bytes * out - pouter to output bytes */ int sqlcipher_page_cipher(codec_ctx *ctx, int for_ctx, Pgno pgno, int mode, int page_sz, unsigned char *in, unsigned char *out) { cipher_ctx *c_ctx = for_ctx ? ctx->write_ctx : ctx->read_ctx; unsigned char *iv_in, *iv_out, *hmac_in, *hmac_out, *out_start; int size; /* calculate some required positions into various buffers */ size = page_sz - c_ctx->reserve_sz; /* adjust size to useable size and memset reserve at end of page */ iv_out = out + size; iv_in = in + size; /* hmac will be written immediately after the initialization vector. the remainder of the page reserve will contain random bytes. note, these pointers are only valid when using hmac */ hmac_in = in + size + c_ctx->iv_sz; hmac_out = out + size + c_ctx->iv_sz; out_start = out; /* note the original position of the output buffer pointer, as out will be rewritten during encryption */ CODEC_TRACE(("codec_cipher:entered pgno=%d, mode=%d, size=%d\n", pgno, mode, size)); CODEC_HEXDUMP("codec_cipher: input page data", in, page_sz); /* the key size should never be zero. If it is, error out. */ if(c_ctx->key_sz == 0) { CODEC_TRACE(("codec_cipher: error possible context corruption, key_sz is zero for pgno=%d\n", pgno)); sqlcipher_memset(out, 0, page_sz); return SQLITE_ERROR; } if(mode == CIPHER_ENCRYPT) { /* start at front of the reserve block, write random data to the end */ if(c_ctx->provider->random(c_ctx->provider_ctx, iv_out, c_ctx->reserve_sz) != SQLITE_OK) return SQLITE_ERROR; } else { /* CIPHER_DECRYPT */ memcpy(iv_out, iv_in, c_ctx->iv_sz); /* copy the iv from the input to output buffer */ } if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_DECRYPT) && !ctx->skip_read_hmac) { if(sqlcipher_page_hmac(c_ctx, pgno, in, size + c_ctx->iv_sz, hmac_out) != SQLITE_OK) { sqlcipher_memset(out, 0, page_sz); CODEC_TRACE(("codec_cipher: hmac operations failed for pgno=%d\n", pgno)); return SQLITE_ERROR; } CODEC_TRACE(("codec_cipher: comparing hmac on in=%p out=%p hmac_sz=%d\n", hmac_in, hmac_out, c_ctx->hmac_sz)); if(sqlcipher_memcmp(hmac_in, hmac_out, c_ctx->hmac_sz) != 0) { /* the hmac check failed */ if(sqlcipher_ismemset(in, 0, page_sz) == 0) { /* first check if the entire contents of the page is zeros. If so, this page resulted from a short read (i.e. sqlite attempted to pull a page after the end of the file. these short read failures must be ignored for autovaccum mode to work so wipe the output buffer and return SQLITE_OK to skip the decryption step. */ CODEC_TRACE(("codec_cipher: zeroed page (short read) for pgno %d, encryption but returning SQLITE_OK\n", pgno)); sqlcipher_memset(out, 0, page_sz); return SQLITE_OK; } else { /* if the page memory is not all zeros, it means the there was data and a hmac on the page. since the check failed, the page was either tampered with or corrupted. wipe the output buffer, and return SQLITE_ERROR to the caller */ CODEC_TRACE(("codec_cipher: hmac check failed for pgno=%d returning SQLITE_ERROR\n", pgno)); sqlcipher_memset(out, 0, page_sz); return SQLITE_ERROR; } } } c_ctx->provider->cipher(c_ctx->provider_ctx, mode, c_ctx->key, c_ctx->key_sz, iv_out, in, size, out); if((c_ctx->flags & CIPHER_FLAG_HMAC) && (mode == CIPHER_ENCRYPT)) { sqlcipher_page_hmac(c_ctx, pgno, out_start, size + c_ctx->iv_sz, hmac_out); } CODEC_HEXDUMP("codec_cipher: output page data", out_start, page_sz); return SQLITE_OK; }