From fd177b0eff5862b24e4f1562f518f93e7c412ef4 Mon Sep 17 00:00:00 2001 From: Zofia Abramowska Date: Thu, 29 May 2014 10:45:39 +0200 Subject: [PATCH] Adapt SQLConnection to sqlcipher Added public methods for key setting and resetting using sqlcipher sqlite3_key and sqlite3_rekey functions. Change-Id: I8a1136beb1bb9b962b72635c254eb211237fc851 --- src/manager/dpl/db/include/dpl/db/sql_connection.h | 40 +++++++++++--- src/manager/dpl/db/src/sql_connection.cpp | 63 ++++++++++++++++++++-- 2 files changed, 94 insertions(+), 9 deletions(-) diff --git a/src/manager/dpl/db/include/dpl/db/sql_connection.h b/src/manager/dpl/db/include/dpl/db/sql_connection.h index 4d0590c..41b0c11 100644 --- a/src/manager/dpl/db/include/dpl/db/sql_connection.h +++ b/src/manager/dpl/db/include/dpl/db/sql_connection.h @@ -33,6 +33,7 @@ #include #include #include +#include namespace CKM { namespace DB { @@ -53,6 +54,7 @@ class SqlConnection DECLARE_EXCEPTION_TYPE(Base, ConnectionBroken) DECLARE_EXCEPTION_TYPE(Base, InternalError) DECLARE_EXCEPTION_TYPE(Base, InvalidColumn) + DECLARE_EXCEPTION_TYPE(Base, InvalidArguments) }; typedef int ColumnIndex; @@ -372,12 +374,7 @@ class SqlConnection enum Option { RO = SQLCIPHER_OPEN_NOMUTEX | SQLCIPHER_OPEN_READONLY, - /** - * *TODO: please remove CREATE option from RW flag when all places - * that need that switched do CRW - */ - RW = SQLCIPHER_OPEN_NOMUTEX | SQLCIPHER_OPEN_READWRITE | - SQLCIPHER_OPEN_CREATE, + RW = SQLCIPHER_OPEN_NOMUTEX | SQLCIPHER_OPEN_READWRITE, CRW = RW | SQLCIPHER_OPEN_CREATE }; }; @@ -416,6 +413,8 @@ class SqlConnection // Synchronization object std::unique_ptr m_synchronizationObject; + bool m_isKeySet; + virtual void Connect(const std::string &address, Flag::Option = Flag::RO); virtual void Disconnect(); @@ -448,6 +447,35 @@ class SqlConnection virtual ~SqlConnection(); /** + * Added extension for encryption functionality: + * + * SetKey gives sqlcipher key, which will be used to encrypt the database + * This function will only fail because of invalid arguments. To check if + * database can be opened with provided key, it is necessary to perform + * some operation on the database (i.e. read from it) and confirm if it + * succeeds. + * Password must have length >= 1. + * + * @param rawPass password given in raw binary format + */ + void SetKey(const std::vector &rawPass); + + /** + * ResetKey is used for changing key used for database encryption. + * If key was already set by using SetKey, this function will only change it. + * If no key was yet set, this function first will set key with rawPassOld and + * then change it to rawPassNew. + * Same rules for failing apply as for SetKey. + * Both password must have length >=1. + * + * @param rawPassOld current password for encryption in raw binary format + * @param rawPassNew new password for encryption in raw binary format + * + */ + void ResetKey(const std::vector &rawPassOld, + const std::vector &rawPassNew); + + /** * Execute SQL command without result * * @param format diff --git a/src/manager/dpl/db/src/sql_connection.cpp b/src/manager/dpl/db/src/sql_connection.cpp index 1a1a10e..8015bcc 100644 --- a/src/manager/dpl/db/src/sql_connection.cpp +++ b/src/manager/dpl/db/src/sql_connection.cpp @@ -368,7 +368,7 @@ void SqlConnection::DataCommand::Reset() { /* * According to: - * http://www.sqlite.org/c3ref/stmt.html + * http://www.sqllite.org/c3ref/stmt.html * * if last sqlcipher3_step command on this stmt returned an error, * then sqlcipher3_reset will return that error, althought it is not an error. @@ -625,6 +625,62 @@ void SqlConnection::Connect(const std::string &address, TurnOnForeignKeys(); } +static std::string rawToHexString(const std::vector &raw) { + std::string str(raw.size() * 2, '0'); + for (std::size_t i = 0; i < raw.size(); i++) + sprintf(&str[i*2], "%02X", raw[i]); + return str; +} + +void SqlConnection::SetKey(const std::vector &rawPass){ + if (m_connection == NULL) { + LogPedantic("Cannot set key. No connection to DB!"); + return; + } + std::string pass = "x'" + rawToHexString(rawPass) + "'"; + int result = sqlcipher3_key(m_connection, pass.c_str(), pass.length()); + if (result == SQLCIPHER_OK) { + LogPedantic("Set key on DB"); + } else { + //sqlcipher3_key fails only when m_connection == NULL || key == NULL || + // key length == 0 + LogPedantic("Failed to set key on DB"); + ThrowMsg(Exception::InvalidArguments, result); + } + + m_isKeySet = true; +}; + +void SqlConnection::ResetKey(const std::vector &rawPassOld, + const std::vector &rawPassNew) { + if (m_connection == NULL) { + LogPedantic("Cannot reset key. No connection to DB!"); + return; + } + // sqlcipher3_rekey requires for key to be already set + if (!m_isKeySet) + SetKey(rawPassOld); + + std::string pass = "x'" + rawToHexString(rawPassNew) + "'"; + int result = sqlcipher3_rekey(m_connection, pass.c_str(), pass.length()); + if (result == SQLCIPHER_OK) { + LogPedantic("Reset key on DB"); + } else { + //sqlcipher3_rekey fails only when m_connection == NULL || key == NULL || + // key length == 0 + LogPedantic("Failed to reset key on DB"); + ThrowMsg(Exception::InvalidArguments, result); + } + //Wiping out the key (required) + pass.assign(pass.length(), '0'); + for(std::size_t i = 0; i < pass.length(); i++) { + if(pass[i] != '0') { + LogPedantic("Wiping key failed."); + ThrowMsg(Exception::InternalError, "Wiping key failed"); + } + } +} + void SqlConnection::Disconnect() { if (m_connection == NULL) { @@ -663,7 +719,7 @@ bool SqlConnection::CheckTableExist(const char *tableName) } DataCommandAutoPtr command = - PrepareDataCommand("select tbl_name from sqlite_master where name=?;"); + PrepareDataCommand("select tbl_name from sqlcipher_master where name=?;"); command->BindString(1, tableName); @@ -680,7 +736,8 @@ SqlConnection::SqlConnection(const std::string &address, SynchronizationObject *synchronizationObject) : m_connection(NULL), m_dataCommandsCount(0), - m_synchronizationObject(synchronizationObject) + m_synchronizationObject(synchronizationObject), + m_isKeySet(false) { LogPedantic("Opening database connection to: " << address); -- 2.7.4