moved the key handling to a new function, added a check for an encrypted database

This commit is contained in:
Jorrit Wronski 2016-12-22 17:52:06 +01:00
parent 611ab22458
commit 685ff293c5
2 changed files with 97 additions and 48 deletions

View File

@ -84,15 +84,13 @@ public:
* @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE...
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout())
* @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default * @param[in] apVfs UTF-8 name of custom VFS to use, or nullptr for sqlite3 default
* @param[in] apPass Key to decrypt (end encrypt) the database, or nullptr for unencrypted databases
* *
* @throw SQLite::Exception in case of error * @throw SQLite::Exception in case of error
*/ */
Database(const char* apFilename, Database(const char* apFilename,
const int aFlags = SQLite::OPEN_READONLY, const int aFlags = SQLite::OPEN_READONLY,
const int aBusyTimeoutMs = 0, const int aBusyTimeoutMs = 0,
const char* apVfs = NULL, const char* apVfs = NULL);
const char* apPass = NULL);
/** /**
* @brief Open the provided database UTF-8 filename. * @brief Open the provided database UTF-8 filename.
@ -108,15 +106,13 @@ public:
* @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE... * @param[in] aFlags SQLite::OPEN_READONLY/SQLite::OPEN_READWRITE/SQLite::OPEN_CREATE...
* @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout()) * @param[in] aBusyTimeoutMs Amount of milliseconds to wait before returning SQLITE_BUSY (see setBusyTimeout())
* @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default * @param[in] aVfs UTF-8 name of custom VFS to use, or empty string for sqlite3 default
* @param[in] aPass Key to decrypt (end encrypt) the database, or empty string for unencrypted databases
* *
* @throw SQLite::Exception in case of error * @throw SQLite::Exception in case of error
*/ */
Database(const std::string& aFilename, Database(const std::string& aFilename,
const int aFlags = SQLite::OPEN_READONLY, const int aFlags = SQLite::OPEN_READONLY,
const int aBusyTimeoutMs = 0, const int aBusyTimeoutMs = 0,
const std::string& aVfs = "", const std::string& aVfs = "");
const std::string& aPass = "");
/** /**
* @brief Close the SQLite database connection. * @brief Close the SQLite database connection.
@ -373,6 +369,46 @@ public:
*/ */
void loadExtension(const char* apExtensionName, const char* apEntryPointName); void loadExtension(const char* apExtensionName, const char* apEntryPointName);
/**
* @brief Set the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_key call and should thus be called
* directly after opening the database. If the database is unencrypted,
* this methods encrypts it immediately.
*
* @param[in] key Key to decode/encode the database
*
* @throw SQLite::Exception in case of error
*/
void key(const std::string& aKey) const noexcept; // nothrow
/**
* @brief Reset the key for the current sqlite database instance.
*
* This is the equivalent of the sqlite3_rekey call and should thus be called
* after the database has been opened with a valid key. To decrypt a
* database, call this method with a NULL pointer.
*
* @param[in] nkey New key to encode the database
*
* @throw SQLite::Exception in case of error
*/
void rekey(const std::string& aNewKey) const noexcept; // nothrow
/**
* @brief Test if a file contains an unencrypted database.
*
* This is a simple test that reads the first bytes of a database file and
* compares them to the standard header for unencrypted databases. If the
* header does not match the standard string, we assume that we have an
* encrypted file.
*
* @param[in] aFilename path/uri to a file
*
* @return true if the database has the standard header.
*/
const bool isUnencrypted(const std::string& aFilename) const noexcept; // nothrow
private: private:
/// @{ Database must be non-copyable /// @{ Database must be non-copyable
Database(const Database&); Database(const Database&);

View File

@ -15,6 +15,7 @@
#include <SQLiteCpp/Exception.h> #include <SQLiteCpp/Exception.h>
#include <sqlite3.h> #include <sqlite3.h>
#include <fstream>
#ifndef SQLITE_DETERMINISTIC #ifndef SQLITE_DETERMINISTIC
#define SQLITE_DETERMINISTIC 0x800 #define SQLITE_DETERMINISTIC 0x800
@ -51,8 +52,7 @@ int getLibVersionNumber() noexcept // nothrow
Database::Database(const char* apFilename, Database::Database(const char* apFilename,
const int aFlags /* = SQLite::OPEN_READONLY*/, const int aFlags /* = SQLite::OPEN_READONLY*/,
const int aBusyTimeoutMs /* = 0 */, const int aBusyTimeoutMs /* = 0 */,
const char* apVfs /* = NULL*/, const char* apVfs /* = NULL*/) :
const char* apPass /* = NULL*/) :
mpSQLite(NULL), mpSQLite(NULL),
mFilename(apFilename) mFilename(apFilename)
{ {
@ -63,25 +63,6 @@ Database::Database(const char* apFilename,
sqlite3_close(mpSQLite); // close is required even in case of error on opening sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception; throw exception;
} }
#ifdef SQLITE_HAS_CODEC
if (apPass != NULL)
{
ret = sqlite3_key(mpSQLite, apPass, strlen(apPass));
if (SQLITE_OK != ret)
{
const SQLite::Exception exception(mpSQLite, ret); // must create before closing
sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception;
}
}
#else
if (apPass != NULL)
{
const SQLite::Exception exception("No encryption support, recompile with SQLITE_HAS_CODEC or remove the password from the constructor."); // must create before closing
sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception;
}
#endif
if (aBusyTimeoutMs > 0) if (aBusyTimeoutMs > 0)
{ {
setBusyTimeout(aBusyTimeoutMs); setBusyTimeout(aBusyTimeoutMs);
@ -92,8 +73,7 @@ Database::Database(const char* apFilename,
Database::Database(const std::string& aFilename, Database::Database(const std::string& aFilename,
const int aFlags /* = SQLite::OPEN_READONLY*/, const int aFlags /* = SQLite::OPEN_READONLY*/,
const int aBusyTimeoutMs /* = 0 */, const int aBusyTimeoutMs /* = 0 */,
const std::string& aVfs /* = "" */, const std::string& aVfs /* = "" */) :
const std::string& aPass /* = "" */) :
mpSQLite(NULL), mpSQLite(NULL),
mFilename(aFilename) mFilename(aFilename)
{ {
@ -104,25 +84,6 @@ Database::Database(const std::string& aFilename,
sqlite3_close(mpSQLite); // close is required even in case of error on opening sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception; throw exception;
} }
#ifdef SQLITE_HAS_CODEC
if (!aPass.empty())
{
ret = sqlite3_key(mpSQLite, aPass.data(), aPass.size());
if (SQLITE_OK != ret)
{
const SQLite::Exception exception(mpSQLite, ret); // must create before closing
sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception;
}
}
#else
if (!aPass.empty())
{
const SQLite::Exception exception("No encryption support, recompile with SQLITE_HAS_CODEC or remove the password from the constructor."); // must create before closing
sqlite3_close(mpSQLite); // close is required even in case of error on opening
throw exception;
}
#endif
if (aBusyTimeoutMs > 0) if (aBusyTimeoutMs > 0)
{ {
setBusyTimeout(aBusyTimeoutMs); setBusyTimeout(aBusyTimeoutMs);
@ -267,4 +228,56 @@ void Database::loadExtension(const char* apExtensionName, const char *apEntryPoi
#endif #endif
} }
//Set the key for the current sqlite database instance.
void Database::key(const std::string& aKey) const noexcept // nothrow
{
int pass_len = aKey.length();
#ifdef SQLITE_HAS_CODEC
if (pass_len > 0) {
int ret = sqlite3_key(mpSQLite, aKey.c_str(), pass_len);
check(ret);
}
#else
if (pass_len > 0) {
const SQLite::Exception exception("No encryption support, recompile with SQLITE_HAS_CODEC to use this function.");
throw exception;
}
#endif // SQLITE_HAS_CODEC
}
// Reset the key for the current sqlite database instance.
void Database::rekey(const std::string& aNewKey) const noexcept // nothrow
{
#ifdef SQLITE_HAS_CODEC
int pass_len = aNewKey.length();
if (pass_len > 0) {
int ret = sqlite3_rekey(mpSQLite, aNewKey.c_str(), pass_len);
check(ret);
} else {
int ret = sqlite3_rekey(mpSQLite, nullptr, 0);
check(ret);
}
#else
const SQLite::Exception exception("No encryption support, recompile with SQLITE_HAS_CODEC to use this function.");
throw exception;
#endif // SQLITE_HAS_CODEC
}
//Test if a file contains an unencrypted database.
const bool Database::isUnencrypted(const std::string& aFilename) const noexcept // nothrow
{
bool encrypted_db = false;
if (aFilename.length() > 0) {
std::ifstream fileBuffer(aFilename, std::ios::in | std::ios::binary);
char header[16];
if (fileBuffer.is_open()) {
fileBuffer.seekg(0, std::ios::beg);
fileBuffer.getline(header, 16);
fileBuffer.close();
}
encrypted_db = strncmp(header, "SQLite format 3\000", 16) != 0;
}
return encrypted_db;
}
} // namespace SQLite } // namespace SQLite