Adapt SQLConnection to sqlcipher
authorZofia Abramowska <z.abramowska@samsung.com>
Thu, 29 May 2014 08:45:39 +0000 (10:45 +0200)
committerBartlomiej Grzelewski <b.grzelewski@samsung.com>
Fri, 12 Sep 2014 12:57:08 +0000 (14:57 +0200)
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
src/manager/dpl/db/src/sql_connection.cpp

index 4d0590c..41b0c11 100644 (file)
@@ -33,6 +33,7 @@
 #include <string>
 #include <dpl/assert.h>
 #include <stdint.h>
+#include <vector>
 
 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<SynchronizationObject> 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<unsigned char> &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<unsigned char> &rawPassOld,
+                  const std::vector<unsigned char> &rawPassNew);
+
+    /**
      * Execute SQL command without result
      *
      * @param format
index 1a1a10e..8015bcc 100644 (file)
@@ -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<unsigned char> &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<unsigned char> &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<unsigned char> &rawPassOld,
+                             const std::vector<unsigned char> &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);