Skip to content

Commit 1ba1ef4

Browse files
authored
Merge pull request #77 from zauguin/sqlcipher
[RFC] Minimal SQLCipher support
2 parents f9e8c40 + 9eb5035 commit 1ba1ef4

File tree

6 files changed

+192
-4
lines changed

6 files changed

+192
-4
lines changed

.travis.yml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,10 @@ addons:
1010
- gcc-5
1111
- g++-5
1212
- libsqlite3-dev
13+
- libsqlcipher-dev
1314
- libboost-all-dev
1415

1516
before_install:
1617
- export CXX="g++-5" CC="gcc-5"
18+
19+
script: ./configure && make test && make clean && make LDFLAGS="-lsqlcipher -DENABLE_SQLCIPHER_TESTS" test

Makefile.in

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -84,7 +84,7 @@ tests/%.result_: tests/%.test
8484
a=$$? ;\
8585
if [ $$a != 0 ]; \
8686
then \
87-
if [ $$a -ge 128 and ] ; \
87+
if [ $$a -ge 128 ] ; \
8888
then \
8989
echo Crash!! > $@ ; \
9090
elif [ $$a -eq 42 ] ;\

README.md

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -309,6 +309,47 @@ NDK support
309309
Just Make sure you are using the full path of your database file :
310310
`sqlite::database db("/data/data/com.your.package/dbfile.db")`.
311311

312+
SQLCipher
313+
----
314+
315+
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.
316+
Then you can create a `sqlcipher_database`.
317+
318+
```c++
319+
#include<iostream>
320+
#include <sqlite_modern_cpp/sqlcipher.h>
321+
using namespace sqlite;
322+
using namespace std;
323+
324+
int main() {
325+
326+
try {
327+
// creates a database file 'dbfile.db' if it does not exists with password 'secret'
328+
sqlcipher_config config;
329+
config.key = secret;
330+
sqlcipher_database db("dbfile.db", config);
331+
332+
// executes the query and creates a 'user' table
333+
db <<
334+
"create table if not exists user ("
335+
" _id integer primary key autoincrement not null,"
336+
" age int,"
337+
" name text,"
338+
" weight real"
339+
");";
340+
341+
// More queries
342+
343+
db.rekey("new_secret"); // Change the password of the already encrypted database.
344+
345+
// Even more queries
346+
}
347+
catch (exception& e) {
348+
cout << e.what() << endl;
349+
}
350+
}
351+
```
352+
312353
Building and Installing
313354
----
314355

hdr/sqlite_modern_cpp.h

Lines changed: 13 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -232,7 +232,7 @@ namespace sqlite {
232232
std::is_floating_point<Type>::value
233233
|| std::is_integral<Type>::value
234234
|| std::is_same<sqlite_int64, Type>::value
235-
> { };
235+
> { };
236236

237237

238238
template<typename T> friend database_binder& operator <<(database_binder& db, const T& val);
@@ -318,8 +318,11 @@ namespace sqlite {
318318
}
319319
};
320320

321+
struct sqlite_config {
322+
};
323+
321324
class database {
322-
private:
325+
protected:
323326
std::shared_ptr<sqlite3> _db;
324327

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

348+
database(const std::string &db_name, const sqlite_config &config): database(db_name) {
349+
(void)config; // Suppress unused warning
350+
}
351+
352+
database(const std::u16string &db_name, const sqlite_config &config): database(db_name) {
353+
(void)config; // Suppress unused warning
354+
}
355+
345356
database_binder operator<<(const std::string& sql) {
346357
return database_binder(_db, sql);
347358
}
@@ -363,7 +374,6 @@ namespace sqlite {
363374
sqlite3_int64 last_insert_rowid() const {
364375
return sqlite3_last_insert_rowid(_db.get());
365376
}
366-
367377
};
368378

369379
template<std::size_t Count>

hdr/sqlite_modern_cpp/sqlcipher.h

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#pragma once
2+
3+
#ifndef SQLITE_HAS_CODEC
4+
#define SQLITE_HAS_CODEC
5+
#endif
6+
7+
#include "../sqlite_modern_cpp.h"
8+
9+
namespace sqlite {
10+
struct sqlcipher_config : public sqlite_config {
11+
std::string key;
12+
};
13+
14+
class sqlcipher_database : public database {
15+
public:
16+
sqlcipher_database(std::string db, const sqlcipher_config &config): database(db, config) {
17+
set_key(config.key);
18+
}
19+
20+
sqlcipher_database(std::u16string db, const sqlcipher_config &config): database(db, config) {
21+
set_key(config.key);
22+
}
23+
24+
void set_key(const std::string &key) {
25+
if(auto ret = sqlite3_key(_db.get(), key.data(), key.size()))
26+
exceptions::throw_sqlite_error(ret);
27+
}
28+
29+
void set_key(const std::string &key, const std::string &db_name) {
30+
if(auto ret = sqlite3_key_v2(_db.get(), db_name.c_str(), key.data(), key.size()))
31+
exceptions::throw_sqlite_error(ret);
32+
}
33+
34+
void rekey(const std::string &new_key) {
35+
if(auto ret = sqlite3_rekey(_db.get(), new_key.data(), new_key.size()))
36+
exceptions::throw_sqlite_error(ret);
37+
}
38+
39+
void rekey(const std::string &new_key, const std::string &db_name) {
40+
if(auto ret = sqlite3_rekey_v2(_db.get(), db_name.c_str(), new_key.data(), new_key.size()))
41+
exceptions::throw_sqlite_error(ret);
42+
}
43+
};
44+
}

tests/sqlcipher.cc

Lines changed: 90 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,90 @@
1+
#ifdef ENABLE_SQLCIPHER_TESTS
2+
#include <iostream>
3+
#include <cstdlib>
4+
#include <unistd.h>
5+
#include <sqlite_modern_cpp/sqlcipher.h>
6+
using namespace sqlite;
7+
using namespace std;
8+
9+
struct TmpFile
10+
{
11+
string fname;
12+
13+
TmpFile()
14+
{
15+
char f[]="/tmp/sqlite_modern_cpp_test_XXXXXX";
16+
int fid = mkstemp(f);
17+
close(fid);
18+
19+
fname = f;
20+
}
21+
22+
~TmpFile()
23+
{
24+
unlink(fname.c_str());
25+
}
26+
};
27+
28+
int main()
29+
{
30+
try
31+
{
32+
TmpFile file;
33+
sqlcipher_config config;
34+
{
35+
config.key = "DebugKey";
36+
sqlcipher_database db(file.fname, config);
37+
38+
db << "CREATE TABLE foo (a integer, b string);";
39+
db << "INSERT INTO foo VALUES (?, ?)" << 1 << "hello";
40+
db << "INSERT INTO foo VALUES (?, ?)" << 2 << "world";
41+
42+
string str;
43+
db << "SELECT b from FOO where a=?;" << 2 >> str;
44+
45+
if(str != "world")
46+
{
47+
cout << "Bad result on line " << __LINE__ << endl;
48+
exit(EXIT_FAILURE);
49+
}
50+
}
51+
try {
52+
config.key = "DebugKey2";
53+
sqlcipher_database db(file.fname, config);
54+
db << "INSERT INTO foo VALUES (?, ?)" << 3 << "fail";
55+
56+
cout << "Can open with wrong key";
57+
exit(EXIT_FAILURE);
58+
} catch(exceptions::notadb) {
59+
// Expected, wrong key
60+
}
61+
{
62+
config.key = "DebugKey";
63+
sqlcipher_database db(file.fname, config);
64+
db.rekey("DebugKey2");
65+
}
66+
{
67+
config.key = "DebugKey2";
68+
sqlcipher_database db(file.fname, config);
69+
db << "INSERT INTO foo VALUES (?, ?)" << 3 << "fail";
70+
}
71+
}
72+
catch(sqlite_exception e)
73+
{
74+
cout << "Unexpected error " << e.what() << endl;
75+
exit(EXIT_FAILURE);
76+
}
77+
catch(...)
78+
{
79+
cout << "Unknown error\n";
80+
exit(EXIT_FAILURE);
81+
}
82+
83+
cout << "OK\n";
84+
exit(EXIT_SUCCESS);
85+
}
86+
#else
87+
int main() {
88+
return 42; //Skip test
89+
}
90+
#endif

0 commit comments

Comments
 (0)