620 lines
23 KiB
C++
620 lines
23 KiB
C++
|
|
// Copyright (c) 2013-2015 IONU Security, Inc. All rights reserved
|
||
|
|
//
|
||
|
|
// Encrypted DB class for off-line cache and other local database storage
|
||
|
|
|
||
|
|
#include <cstring>
|
||
|
|
#include <iostream>
|
||
|
|
#include <string>
|
||
|
|
#include <sstream>
|
||
|
|
#include <memory>
|
||
|
|
#include "eyeencrypteddb.h"
|
||
|
|
#include "eyelog.h"
|
||
|
|
#include "eyeutils.h"
|
||
|
|
|
||
|
|
using namespace sequencelogic;
|
||
|
|
|
||
|
|
namespace sequencelogic {
|
||
|
|
static char IONU_EYEDB_SALT[] = "74734f616e6c554949556c6e614f7374";
|
||
|
|
}
|
||
|
|
|
||
|
|
EyeEncryptedDB::EyeEncryptedDB (const std::string& filename, const unsigned char* key)
|
||
|
|
:EyeDB()
|
||
|
|
{
|
||
|
|
_filename = "";
|
||
|
|
_DEK = NULL;
|
||
|
|
_IV = NULL;
|
||
|
|
_db = NULL;
|
||
|
|
EVP_CIPHER_CTX_init (&_ctx);
|
||
|
|
|
||
|
|
int ec = SQLITE_OK;
|
||
|
|
std::string sql;
|
||
|
|
char* errmsg = NULL;
|
||
|
|
sqlite3_stmt* stmt = NULL;
|
||
|
|
int oflags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_FULLMUTEX;
|
||
|
|
int cflags = SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_FULLMUTEX;
|
||
|
|
size_t bytes = 0;
|
||
|
|
unsigned char* ciphertext = NULL;
|
||
|
|
char magic[256];
|
||
|
|
|
||
|
|
if (key && sequencelogic::IsValidFilename (filename)) {
|
||
|
|
unsigned char salt[17];
|
||
|
|
sequencelogic::HexToBinary (IONU_EYEDB_SALT, salt);
|
||
|
|
salt[16] = '\0';
|
||
|
|
//printf ("Salt: %s\n", (char*)salt);
|
||
|
|
_filename = filename;
|
||
|
|
_DEK = new unsigned char[SL_AES_KEY_LEN + SL_AES_BLOCK_LEN + 4];
|
||
|
|
_IV = new unsigned char[SL_AES_BLOCK_LEN + 4];
|
||
|
|
|
||
|
|
if (sqlite3_open_v2 (filename.c_str(), &_db, oflags, NULL) != SQLITE_OK) {
|
||
|
|
// Close this and create new db
|
||
|
|
if (_db) sqlite3_close_v2 (_db);
|
||
|
|
if (sqlite3_open_v2 (filename.c_str(), &_db, cflags, NULL) != SQLITE_OK) {
|
||
|
|
delete[] _DEK;
|
||
|
|
_DEK = NULL;
|
||
|
|
IONUDEBUG ("EyeEncryptedDB(%s) - Create failed", filename.c_str());
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
// Create the "Header" table and insert Version, Magic, DBK, IV
|
||
|
|
sql = "CREATE TABLE Header (Parameter TEXT UNIQUE, Value TEXT);";
|
||
|
|
if (sqlite3_exec (_db, sql.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - CREATE TABLE Header - %s\n", errmsg);
|
||
|
|
}
|
||
|
|
sql = "INSERT INTO Header VALUES ('Version', '1.0');";
|
||
|
|
if (sqlite3_exec (_db, sql.c_str(), NULL, NULL, &errmsg) != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT INTO Header Version - %s\n", errmsg);
|
||
|
|
}
|
||
|
|
|
||
|
|
// Generate random values for DEK (Database Encryption Key) and IV
|
||
|
|
if (sequencelogic::RandomBytes (SL_AES_KEY_LEN, _DEK)) {
|
||
|
|
ciphertext = new unsigned char [SL_AES_KEY_LEN + SL_AES_BLOCK_LEN];
|
||
|
|
bytes = sequencelogic::Encryptor (&_ctx, SL_AES_KEY_LEN, _DEK, ciphertext, key, salt);
|
||
|
|
sql = "INSERT INTO Header VALUES ('DEK', ?);";
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of DEK failed, prepare: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_bind_blob(stmt, 1, (const void*)ciphertext, (int)bytes, SQLITE_TRANSIENT);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of DEK failed, bind: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_step (stmt);
|
||
|
|
if (ec != SQLITE_DONE && ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of DEK failed, step: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_clear_bindings(stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of DEK failed, clear: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of DEK failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
//sequencelogic::Base64Encode (ciphertext, bytes, b64);
|
||
|
|
//printf ("dek: %s\n", b64);
|
||
|
|
delete[] ciphertext;
|
||
|
|
ciphertext = NULL;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - DEK generation failed");
|
||
|
|
}
|
||
|
|
if (sequencelogic::RandomBytes (SL_AES_BLOCK_LEN, _IV)) {
|
||
|
|
sql = "INSERT INTO Header VALUES ('IV', ?);";
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of IV failed, prepare: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_bind_blob(stmt, 1, (const void*)_IV, SL_AES_BLOCK_LEN, SQLITE_TRANSIENT);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of IV failed, bind: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_step (stmt);
|
||
|
|
if (ec != SQLITE_DONE && ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of IV failed, step: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_clear_bindings(stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of IV failed, clear: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of IV failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - IV generation failed");
|
||
|
|
}
|
||
|
|
// Encrypt the magic value and insert into Header
|
||
|
|
bytes = sequencelogic::Encryptor (&_ctx, 5, (unsigned char*)"IOnU", (unsigned char*)magic, _DEK, _IV);
|
||
|
|
//sequencelogic::Base64Encode ((unsigned char*)magic, bytes, b64);
|
||
|
|
//printf ("Encrypted Magic, %d bytes, %s\n", (int)bytes, b64);
|
||
|
|
sql = "INSERT INTO Header VALUES ('Magic', ?);";
|
||
|
|
//std::cout << sql << std::endl;
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of Magic failed, prepare: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_bind_blob(stmt, 1, (const void*)magic, (int)bytes, SQLITE_TRANSIENT);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of Magic failed, bind: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_step (stmt);
|
||
|
|
if (ec != SQLITE_DONE && ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of Magic failed, step: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_clear_bindings(stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of Magic failed, clear: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - INSERT of Magic failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
|
||
|
|
}
|
||
|
|
//sequencelogic::Base64Encode (_DEK, SL_AES_KEY_LEN, b64);
|
||
|
|
//printf ("DEK: %s\n", b64);
|
||
|
|
//sequencelogic::Base64Encode (_IV, SL_AES_BLOCK_LEN, b64);
|
||
|
|
//printf ("IV: %s\n", b64);
|
||
|
|
}
|
||
|
|
// Opened existing db
|
||
|
|
else {
|
||
|
|
sql = "SELECT * FROM Header WHERE Parameter == 'DEK';";
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of DEK failed, prepare: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_step (stmt);
|
||
|
|
if (ec != SQLITE_ROW) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of DEK failed, step: %d", ec);
|
||
|
|
}
|
||
|
|
bytes = sqlite3_column_bytes (stmt, 1);
|
||
|
|
ciphertext = (unsigned char*) sqlite3_column_blob (stmt, 1);
|
||
|
|
if (ciphertext) {
|
||
|
|
bytes = Decryptor (&_ctx, bytes, ciphertext, _DEK, key, salt);
|
||
|
|
if (bytes != SL_AES_KEY_LEN) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - get of DEK failed, wrong number of bytes: %d", (int)bytes);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - get of DEK failed");
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of DEK failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
|
||
|
|
sql = "SELECT * FROM Header WHERE Parameter == 'IV';";
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of IV failed: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_step (stmt);
|
||
|
|
if (ec != SQLITE_ROW) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of IV failed, step: %d", ec);
|
||
|
|
}
|
||
|
|
unsigned char* iv = (unsigned char*) sqlite3_column_blob (stmt, 1);
|
||
|
|
bytes = sqlite3_column_bytes (stmt, 1);
|
||
|
|
if (iv && bytes == SL_AES_BLOCK_LEN) {
|
||
|
|
sequencelogic::MemCpy (_IV, iv, SL_AES_BLOCK_LEN);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - get of IV failed %d bytes", bytes);
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of IV failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
|
||
|
|
sql = "SELECT * FROM Header WHERE Parameter == 'Magic';";
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of Magic failed, prepare: %d", ec);
|
||
|
|
}
|
||
|
|
ec = sqlite3_step (stmt);
|
||
|
|
if (ec != SQLITE_ROW) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of Magic failed, step: %d", ec);
|
||
|
|
}
|
||
|
|
bytes = sqlite3_column_bytes (stmt, 1);
|
||
|
|
ciphertext = (unsigned char*) sqlite3_column_blob (stmt, 1);
|
||
|
|
bool goodMagic = false;
|
||
|
|
if (ciphertext) {
|
||
|
|
bytes = Decryptor (&_ctx, bytes, ciphertext, (unsigned char*)magic, _DEK, _IV);
|
||
|
|
if (bytes == 5 && sequencelogic::StrCmp (magic, "IOnU") == 0)
|
||
|
|
goodMagic = true;
|
||
|
|
}
|
||
|
|
if (!goodMagic) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - bad magic");
|
||
|
|
if (_DEK) {
|
||
|
|
delete[] _DEK;
|
||
|
|
_DEK = NULL;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB() - SELECT of Magic failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
|
||
|
|
//sequencelogic::Base64Encode (_DEK, SL_AES_KEY_LEN, b64);
|
||
|
|
//printf ("DEK: %s\n", b64);
|
||
|
|
//sequencelogic::Base64Encode (_IV, SL_AES_BLOCK_LEN, b64);
|
||
|
|
//printf ("IV: %s\n", b64);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
EyeEncryptedDB::~EyeEncryptedDB ()
|
||
|
|
{
|
||
|
|
if (_DEK) {
|
||
|
|
sequencelogic::MemSet (_DEK, 0, SL_AES_KEY_LEN);
|
||
|
|
delete[] _DEK;
|
||
|
|
_DEK = NULL;
|
||
|
|
}
|
||
|
|
if (_IV) {
|
||
|
|
sequencelogic::MemSet (_IV, 0, SL_AES_BLOCK_LEN);
|
||
|
|
delete[] _IV;
|
||
|
|
_IV = NULL;
|
||
|
|
}
|
||
|
|
if (_db) {
|
||
|
|
sqlite3_close_v2 (_db);
|
||
|
|
_db = NULL;
|
||
|
|
}
|
||
|
|
EVP_CIPHER_CTX_cleanup (&_ctx);
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeEncryptedDB::Put (const std::string& collection, const std::string& key, const std::string& value)
|
||
|
|
{
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Put() - DB not open");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (collection.size() < 1 || key.size() < 1 || value.size() < 1) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Put() - Invalid collection, key and/or value");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
size_t bytes = 0;
|
||
|
|
size_t len = value.size();
|
||
|
|
|
||
|
|
//create table if necessary
|
||
|
|
std::stringstream table;
|
||
|
|
table << "CREATE TABLE IF NOT EXISTS " << collection << " (id TEXT UNIQUE, value TEXT);";
|
||
|
|
if (!executeQuery(table.str())) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Put(%s, %s) - executeQuery failed", collection.c_str(), key.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//prepare statement
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "INSERT OR REPLACE INTO " << collection << " VALUES (?,?);";
|
||
|
|
statement stmt(*this, sql.str());
|
||
|
|
if (!stmt.isPrepared()) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Put(%s, %s) - isPrepared failed", collection.c_str(), key.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//encrypt value
|
||
|
|
std::unique_ptr<unsigned char[]> ciphertext(new unsigned char[len + 2*SL_AES_BLOCK_LEN]);
|
||
|
|
bytes = sequencelogic::Encryptor (&_ctx, len, (unsigned char*)value.data(), ciphertext.get(), _DEK, _IV);
|
||
|
|
|
||
|
|
//bind arguments and insert row
|
||
|
|
if (stmt.bind(1, key)
|
||
|
|
&& stmt.bind(2, ciphertext.get(), (int)bytes))
|
||
|
|
{
|
||
|
|
int ec = stmt.step();
|
||
|
|
if (ec != SQLITE_ROW && ec != SQLITE_DONE) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Put(%s, %s) - step failed: %d", collection.c_str(), key.c_str(), ec);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
char* EyeEncryptedDB::Get (const std::string& collection, const std::string& key)
|
||
|
|
{
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Get() - DB not open");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
else if (collection.size() < 1 || key.size() < 1) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Get() - Invalid collection and/or key");
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
unsigned char* value = NULL;
|
||
|
|
size_t bytes = 0;
|
||
|
|
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "SELECT * FROM " << collection << " WHERE id == ?";
|
||
|
|
statement stmt(*this, sql.str());
|
||
|
|
if (!stmt.isPrepared()) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Get(%s, %s) - prepare failed", collection.c_str(), key.c_str());
|
||
|
|
return NULL;
|
||
|
|
}
|
||
|
|
|
||
|
|
if (stmt.bind(1, key))
|
||
|
|
{
|
||
|
|
int ec = stmt.step();
|
||
|
|
if (ec == SQLITE_ROW) {
|
||
|
|
//value = sequencelogic::StrDup (sqlite3_column_text (stmt, 1));
|
||
|
|
unsigned char* ciphertext = (unsigned char*) stmt.columnBlob(1);
|
||
|
|
bytes = (size_t) stmt.columnBytesInt(1);
|
||
|
|
//char b64[256];
|
||
|
|
//sequencelogic::Base64Encode (ciphertext, bytes, b64);
|
||
|
|
//printf ("dec: %s.%s %s\n", collection, key, b64);
|
||
|
|
if (ciphertext) {
|
||
|
|
value = new unsigned char[bytes+1];
|
||
|
|
bytes = Decryptor (&_ctx, bytes, ciphertext, value, _DEK, _IV);
|
||
|
|
value[bytes] = '\0';
|
||
|
|
}
|
||
|
|
}
|
||
|
|
else if (ec == SQLITE_DONE) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Get(%s, %s) - key not found", collection.c_str(), key.c_str());
|
||
|
|
} else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Get(%s, %s) - step failed %d", collection.c_str(), key.c_str(), ec);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return (char*)value;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeEncryptedDB::PutAll (const std::string& collection, const std::map<std::string, std::string>& entries, bool remove)
|
||
|
|
{
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Put() - DB not open");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (collection.size() < 1) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::PutAll() - Invalid collection");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
//Create the table if necessary
|
||
|
|
std::stringstream table;
|
||
|
|
table << "CREATE TABLE IF NOT EXISTS " << collection << " (id TEXT UNIQUE, value TEXT);";
|
||
|
|
if (!executeQuery(table.str())) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
// Protect optional remove and puts in a transaction, on failure this will rollback to unchanged version
|
||
|
|
{
|
||
|
|
transaction transaction(*this);
|
||
|
|
|
||
|
|
//remove existing rows if requested
|
||
|
|
if (remove) {
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "DELETE FROM " << collection;
|
||
|
|
if (!executeQuery(table.str())) {
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
//Create query to insert/replace rows
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "INSERT OR REPLACE INTO " << collection << " VALUES (?, ?)";
|
||
|
|
statement stmt(*this, sql.str());
|
||
|
|
if (!stmt.isPrepared()) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::PutAll(%s) - prepare failed", collection.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
//iterate new key/values and update table
|
||
|
|
for (std::map<std::string, std::string>::const_iterator itr = entries.begin(); itr != entries.end(); ++itr) {
|
||
|
|
//encrypt value
|
||
|
|
size_t len = itr->second.size();
|
||
|
|
std::unique_ptr<unsigned char[]> ciphertext(new unsigned char[len + 2*SL_AES_BLOCK_LEN]);
|
||
|
|
size_t bytes = sequencelogic::Encryptor (&_ctx, len, (unsigned char*) itr->second.c_str(), ciphertext.get(), _DEK, _IV);
|
||
|
|
|
||
|
|
//bind key/value and step
|
||
|
|
if (stmt.bind(1, itr->first)
|
||
|
|
&& stmt.bind(2, ciphertext.get(), (int)bytes))
|
||
|
|
{
|
||
|
|
//insert/update row
|
||
|
|
int result = stmt.step();
|
||
|
|
if (result != SQLITE_DONE) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::PutAll(%s) - insert failed", collection.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
stmt.reset();
|
||
|
|
} else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::PutAll(%s) - bind failed", collection.c_str());
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
transaction.commit();
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
STRING_STRING_MAP EyeEncryptedDB::GetAll (const std::string& collection)
|
||
|
|
{
|
||
|
|
STRING_STRING_MAP entries;
|
||
|
|
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::GetAll() - DB not open");
|
||
|
|
}
|
||
|
|
else if (collection.size() < 1) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::GetAll() - Invalid collection");
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "SELECT * FROM " << collection;
|
||
|
|
statement stmt(*this, sql.str());
|
||
|
|
if (stmt.isPrepared()) {
|
||
|
|
while (stmt.step() == SQLITE_ROW) {
|
||
|
|
//key
|
||
|
|
std::string key = (const char*)(stmt.columnText(0));
|
||
|
|
|
||
|
|
//decrypt value from ciphertext in column
|
||
|
|
unsigned char* ciphertext = (unsigned char*) stmt.columnBlob(1);
|
||
|
|
size_t bytes = (size_t) stmt.columnBytesInt(1);
|
||
|
|
if (ciphertext) {
|
||
|
|
std::unique_ptr<char[]> value(new char[bytes+1]);
|
||
|
|
bytes = Decryptor (&_ctx, bytes, ciphertext, (unsigned char*)value.get(), _DEK, _IV);
|
||
|
|
entries[key] = std::string(value.get(), bytes);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::GetAll() - prepare failed");
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return entries;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeEncryptedDB::ReKey (const unsigned char* newTGIkey)
|
||
|
|
{
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::ReKey() - DB not open");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (!newTGIkey) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::ReKey() - Invalid TGI key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeDB::ReKey() - DB not open");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (!newTGIkey) {
|
||
|
|
IONUDEBUG ("EyeDB::ReKey() - Invalid TGI key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
size_t bytes = 0;
|
||
|
|
unsigned char salt[17];
|
||
|
|
sequencelogic::HexToBinary (IONU_EYEDB_SALT, salt);
|
||
|
|
unsigned char ciphertext[256];
|
||
|
|
bytes = sequencelogic::Encryptor (&_ctx, SL_AES_KEY_LEN, _DEK, ciphertext, newTGIkey, salt);
|
||
|
|
|
||
|
|
std::string sql = "REPLACE INTO Header VALUES ('DEK', ?);";
|
||
|
|
statement stmt(*this, sql);
|
||
|
|
if (stmt.isPrepared() && stmt.bind(1, ciphertext, (int)bytes)) {
|
||
|
|
int ec = stmt.step();
|
||
|
|
if (ec == SQLITE_DONE)
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeEncryptedDB::Remove (const std::string& collection)
|
||
|
|
{
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - DB not open");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (collection.size() < 1) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - Invalid collection");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "DROP TABLE IF EXISTS " << collection;
|
||
|
|
statement stmt(*this, sql.str());
|
||
|
|
if (stmt.isPrepared()) {
|
||
|
|
int ec = stmt.step();
|
||
|
|
if (ec != SQLITE_DONE) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - step failed");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - prepare failed");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
bool EyeEncryptedDB::Remove (const std::string& collection, const std::string& key)
|
||
|
|
{
|
||
|
|
if (!_DEK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - DB not open");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else if (collection.size() < 1 || key.size() < 1) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - Invalid collection and/or key");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
std::stringstream sql;
|
||
|
|
sql << "DELETE FROM " << collection << " WHERE id == ?";
|
||
|
|
statement stmt(*this, sql.str());
|
||
|
|
if (stmt.isPrepared() && stmt.bind(1, key)) {
|
||
|
|
int ec = stmt.step();
|
||
|
|
if (ec != SQLITE_DONE) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - step failed");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
} else {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Remove() - prepare failed");
|
||
|
|
return false;
|
||
|
|
}
|
||
|
|
}
|
||
|
|
return true;
|
||
|
|
}
|
||
|
|
|
||
|
|
void EyeEncryptedDB::Dump (const std::string& collection)
|
||
|
|
{
|
||
|
|
size_t bytes = 0;
|
||
|
|
std::string sql;
|
||
|
|
sqlite3_stmt* stmt = NULL;
|
||
|
|
sql = "SELECT * FROM Header;";
|
||
|
|
int ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Dump() - SELECT * FROM Header failed, prepare: %d", ec);
|
||
|
|
}
|
||
|
|
printf ("Header {\n");
|
||
|
|
while (sqlite3_step (stmt) == SQLITE_ROW) {
|
||
|
|
const unsigned char* key = sqlite3_column_text (stmt, 0);
|
||
|
|
unsigned char* val = (unsigned char*) sqlite3_column_blob (stmt, 1);
|
||
|
|
bytes = (size_t) sqlite3_column_bytes (stmt, 1);
|
||
|
|
if (!sequencelogic::IsStrAscii ((char*) val, bytes)) {
|
||
|
|
char b64[256];
|
||
|
|
sequencelogic::Base64Encode (val, bytes, b64);
|
||
|
|
printf (" %s: %s\n", key, b64);
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
printf (" %s: %s\n", key, (char*)val);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Dump() - SELECT * FROM Header failed, finalize: %d", ec);
|
||
|
|
}
|
||
|
|
printf ("}\n");
|
||
|
|
|
||
|
|
sql = "SELECT * FROM ";
|
||
|
|
sql += collection;
|
||
|
|
sql += ";";
|
||
|
|
ec = sqlite3_prepare_v2 (_db, sql.c_str(), (int)sql.length(), &stmt, 0);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Dump() - SELECT * FROM %s failed, prepare: %d", collection.c_str(), ec);
|
||
|
|
}
|
||
|
|
std::cout << collection << " {" << std::endl;
|
||
|
|
while (sqlite3_step (stmt) == SQLITE_ROW) {
|
||
|
|
const unsigned char* key = sqlite3_column_text (stmt, 0);
|
||
|
|
unsigned char* val = (unsigned char*) sqlite3_column_blob (stmt, 1);
|
||
|
|
bytes = (size_t) sqlite3_column_bytes (stmt, 1);
|
||
|
|
if (!sequencelogic::IsStrAscii ((char*) val, bytes)) {
|
||
|
|
char b64[256];
|
||
|
|
sequencelogic::Base64Encode (val, bytes, b64);
|
||
|
|
std::cout << " " << key << ": " << b64 << std::endl;
|
||
|
|
}
|
||
|
|
else {
|
||
|
|
printf (" %s: %s\n", key, (char*)val);
|
||
|
|
}
|
||
|
|
}
|
||
|
|
ec = sqlite3_finalize (stmt);
|
||
|
|
if (ec != SQLITE_OK) {
|
||
|
|
IONUDEBUG ("EyeEncryptedDB::Dump() - SELECT * FROM %s failed, finalize: %d", collection.c_str(), ec);
|
||
|
|
}
|
||
|
|
printf ("}\n");
|
||
|
|
}
|
||
|
|
|