Skip to content

Commit

Permalink
Merge pull request #77 from zauguin/sqlcipher
Browse files Browse the repository at this point in the history
[RFC] Minimal SQLCipher support
  • Loading branch information
aminroosta authored Feb 16, 2017
2 parents f9e8c40 + 9eb5035 commit 1ba1ef4
Show file tree
Hide file tree
Showing 6 changed files with 192 additions and 4 deletions.
3 changes: 3 additions & 0 deletions .travis.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,10 @@ addons:
- gcc-5
- g++-5
- libsqlite3-dev
- libsqlcipher-dev
- libboost-all-dev

before_install:
- export CXX="g++-5" CC="gcc-5"

script: ./configure && make test && make clean && make LDFLAGS="-lsqlcipher -DENABLE_SQLCIPHER_TESTS" test
2 changes: 1 addition & 1 deletion Makefile.in
Original file line number Diff line number Diff line change
Expand Up @@ -84,7 +84,7 @@ tests/%.result_: tests/%.test
a=$$? ;\
if [ $$a != 0 ]; \
then \
if [ $$a -ge 128 and ] ; \
if [ $$a -ge 128 ] ; \
then \
echo Crash!! > $@ ; \
elif [ $$a -eq 42 ] ;\
Expand Down
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -309,6 +309,47 @@ NDK support
Just Make sure you are using the full path of your database file :
`sqlite::database db("/data/data/com.your.package/dbfile.db")`.

SQLCipher
----

The library has native support for [SQLCipher](https://www.zetetic.net/sqlcipher/). If you want to use encrypted databases, you have to include the `sqlite_moder_cpp/sqlcipher.h` header.
Then you can create a `sqlcipher_database`.

```c++
#include<iostream>
#include <sqlite_modern_cpp/sqlcipher.h>
using namespace sqlite;
using namespace std;

int main() {

try {
// creates a database file 'dbfile.db' if it does not exists with password 'secret'
sqlcipher_config config;
config.key = secret;
sqlcipher_database db("dbfile.db", config);

// executes the query and creates a 'user' table
db <<
"create table if not exists user ("
" _id integer primary key autoincrement not null,"
" age int,"
" name text,"
" weight real"
");";

// More queries

db.rekey("new_secret"); // Change the password of the already encrypted database.

// Even more queries
}
catch (exception& e) {
cout << e.what() << endl;
}
}
```

Building and Installing
----

Expand Down
16 changes: 13 additions & 3 deletions hdr/sqlite_modern_cpp.h
Original file line number Diff line number Diff line change
Expand Up @@ -232,7 +232,7 @@ namespace sqlite {
std::is_floating_point<Type>::value
|| std::is_integral<Type>::value
|| std::is_same<sqlite_int64, Type>::value
> { };
> { };


template<typename T> friend database_binder& operator <<(database_binder& db, const T& val);
Expand Down Expand Up @@ -318,8 +318,11 @@ namespace sqlite {
}
};

struct sqlite_config {
};

class database {
private:
protected:
std::shared_ptr<sqlite3> _db;

public:
Expand All @@ -342,6 +345,14 @@ namespace sqlite {
database(std::shared_ptr<sqlite3> db):
_db(db) {}

database(const std::string &db_name, const sqlite_config &config): database(db_name) {
(void)config; // Suppress unused warning
}

database(const std::u16string &db_name, const sqlite_config &config): database(db_name) {
(void)config; // Suppress unused warning
}

database_binder operator<<(const std::string& sql) {
return database_binder(_db, sql);
}
Expand All @@ -363,7 +374,6 @@ namespace sqlite {
sqlite3_int64 last_insert_rowid() const {
return sqlite3_last_insert_rowid(_db.get());
}

};

template<std::size_t Count>
Expand Down
44 changes: 44 additions & 0 deletions hdr/sqlite_modern_cpp/sqlcipher.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#pragma once

#ifndef SQLITE_HAS_CODEC
#define SQLITE_HAS_CODEC
#endif

#include "../sqlite_modern_cpp.h"

namespace sqlite {
struct sqlcipher_config : public sqlite_config {
std::string key;
};

class sqlcipher_database : public database {
public:
sqlcipher_database(std::string db, const sqlcipher_config &config): database(db, config) {
set_key(config.key);
}

sqlcipher_database(std::u16string db, const sqlcipher_config &config): database(db, config) {
set_key(config.key);
}

void set_key(const std::string &key) {
if(auto ret = sqlite3_key(_db.get(), key.data(), key.size()))
exceptions::throw_sqlite_error(ret);
}

void set_key(const std::string &key, const std::string &db_name) {
if(auto ret = sqlite3_key_v2(_db.get(), db_name.c_str(), key.data(), key.size()))
exceptions::throw_sqlite_error(ret);
}

void rekey(const std::string &new_key) {
if(auto ret = sqlite3_rekey(_db.get(), new_key.data(), new_key.size()))
exceptions::throw_sqlite_error(ret);
}

void rekey(const std::string &new_key, const std::string &db_name) {
if(auto ret = sqlite3_rekey_v2(_db.get(), db_name.c_str(), new_key.data(), new_key.size()))
exceptions::throw_sqlite_error(ret);
}
};
}
90 changes: 90 additions & 0 deletions tests/sqlcipher.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,90 @@
#ifdef ENABLE_SQLCIPHER_TESTS
#include <iostream>
#include <cstdlib>
#include <unistd.h>
#include <sqlite_modern_cpp/sqlcipher.h>
using namespace sqlite;
using namespace std;

struct TmpFile
{
string fname;

TmpFile()
{
char f[]="/tmp/sqlite_modern_cpp_test_XXXXXX";
int fid = mkstemp(f);
close(fid);

fname = f;
}

~TmpFile()
{
unlink(fname.c_str());
}
};

int main()
{
try
{
TmpFile file;
sqlcipher_config config;
{
config.key = "DebugKey";
sqlcipher_database db(file.fname, config);

db << "CREATE TABLE foo (a integer, b string);";
db << "INSERT INTO foo VALUES (?, ?)" << 1 << "hello";
db << "INSERT INTO foo VALUES (?, ?)" << 2 << "world";

string str;
db << "SELECT b from FOO where a=?;" << 2 >> str;

if(str != "world")
{
cout << "Bad result on line " << __LINE__ << endl;
exit(EXIT_FAILURE);
}
}
try {
config.key = "DebugKey2";
sqlcipher_database db(file.fname, config);
db << "INSERT INTO foo VALUES (?, ?)" << 3 << "fail";

cout << "Can open with wrong key";
exit(EXIT_FAILURE);
} catch(exceptions::notadb) {
// Expected, wrong key
}
{
config.key = "DebugKey";
sqlcipher_database db(file.fname, config);
db.rekey("DebugKey2");
}
{
config.key = "DebugKey2";
sqlcipher_database db(file.fname, config);
db << "INSERT INTO foo VALUES (?, ?)" << 3 << "fail";
}
}
catch(sqlite_exception e)
{
cout << "Unexpected error " << e.what() << endl;
exit(EXIT_FAILURE);
}
catch(...)
{
cout << "Unknown error\n";
exit(EXIT_FAILURE);
}

cout << "OK\n";
exit(EXIT_SUCCESS);
}
#else
int main() {
return 42; //Skip test
}
#endif

0 comments on commit 1ba1ef4

Please sign in to comment.