SQLcipher how to create cypted database? - ios

I'm trying to follow this tutorial: http://sqlcipher.net/ios-tutorial/
I create a database called "sqlcipher.db" then I recreate this
When I execute this code:
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
NSString *databasePath = [[NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES) objectAtIndex:0]
stringByAppendingPathComponent: #"encrypted.db"];
sqlite3 *db;
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
const char* key = [#"secret" UTF8String];
int sqlite3_key(sqlite3 *db, const void *pKey, int nKey);
sqlite3_key(db, key, strlen(key));
if (sqlite3_exec(db, (const char*) "SELECT count(*) FROM t1;", NULL, NULL, NULL) == SQLITE_OK) {
// password is correct, or, database has been initialized
NSLog(#"Hello 1");
} else {
// incorrect password!
NSLog(#"Hello 2");
}
sqlite3_close(db);
} else {
NSLog(#"Hello 3");
}
}
It allways outs "Hello 2".
When I try to reproduce the steps to crate an encrypted db described here http://zetetic.net/blog/2009/12/29/how-to-encrypt-a-plaintext-sqlite-database-to-use-sqlcipher.html#disqus_thread I can't get it encrypted, I believe that it is beacause I am using sqlite3 mac command.
So I saw in the comments that S Lombardo says that I have to compile a command line sqlcipher executable but the link doesn't works.
How should I encrypt my database to use it with SQLcipher?
Did anyone have success using sqlicipher in iOS?

After one hour Googling i've found how to compile sqlcipher command line for OSX:
I hope this could help somebody.
https://groups.google.com/forum/#!msg/sqlcipher/bd1R13RpZHQ/SEPK8YrRt1gJ

Related

Google Analytics SDK creates SQLite errors

Updates
19th August 2015 - Bug seems to have now been fixed in the 3.13 update, even though the only thing they list in their changelog is "Fixed an issue which caused crashes when using setCampaignParametersFromUrl". Take that as you will.
8th June 2015 - Still encountering this problem. If I disable automatic sending of events ([GAI sharedInstance].dispatchInterval = -1;) then I still receive errors. I assume, therefore, that the problem lies with inserting the event into the Google Analytics SQLite database, that somehow my own database statement that is currently in progress are becoming void.
10th June 2015 - Still encountering crashes. Tried removing my controllers extending GAITrackedViewController and sending the createScreenView track manually with no change in crash frequency.
25th June 2015 - Still encountering crashes.
Intro
I have added the Google Analytics SDK 3.12 to my iPhone app and everything is working as expected - I run the app and can see all of the hits and events that I have setup coming through on the Web interface.
I am initialising the SDK in my AppDelegate right at the top of my didFinishLaunchingWithOptions, like so:
[[GAI sharedInstance] trackerWithTrackingId:GOOGLE_ANALYTICS_ID];
The problem
However, I have found that running Google Analytics creates errors when I try and use SQLite for myself. They can manifest as serious errors such as:
"Database disk image is malformed" and then insta-crashes
"Disc i/O error" whenever I run a query (though doesn't crash)
And they can also cause my own SQLite queries to just fail, for instance:
if (! sqlite3_prepare_v2(_db, [sql UTF8String], -1, &_statement, NULL) == SQLITE_OK) {`
// ..
// ..
if (sqlite3_step(_statement) == SQLITE_ROW) {
Will result, randomly, in the following error:
sqlite3_prepare_v2 EXC_BAD_ACCESS (code=1, address=0x6800000000)
If I comment out the SDK initialisation then everything goes back to being incredibly stable. Uncomment it again and it will crash the app within a minute.
Pre-emptive question answering
Am running this on a iPhone 6 running 8.3 (12F70).
Have tried uninstalling and reinstalling the app.
I have added all of the pre-requisites for Google Analytics to work; all of the .m files to the library, the libGoogleAnalyticsServices.a file, and also the Linked Frameworks and Libraries.
I also have Crashlytics, but have tried commenting it out from the code ([Fabric with:#[CrashlyticsKit]];) and removing its library from the Linked Frameworks and Libraries with exactly the same results.
Code
Setting up the class
// In didFinishLaunchingWithOptions
[Db setup];
[Db connect];
Accesses the class
Db * db = [[Db alloc] init];
if ([db prepare:#"SELECT * FROM `table` WHERE `id` = ?" withBindings:#[#"123"]]) {
while ([db stepThrough]) {
// ..
}
}
[db finalise];
The class
(Have indicated where the errors appear with comments)
#implementation Db
static sqlite3 * _db;
static NSString * _dbPath;
#pragma mark - Setup
+ (BOOL)setup {
NSString * sqlBundlePath = [[NSBundle mainBundle] pathForResource:#"db" ofType:#"sqlite"];
NSString * documentsFolder = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES)[0];
NSString * sqlDocumentPath = [[documentsFolder stringByAppendingPathComponent:#"db"] stringByAppendingPathExtension:#"sqlite"];
NSFileManager * fileManager = [NSFileManager defaultManager];
if (! [fileManager fileExistsAtPath:sqlDocumentPath]) {
NSError * error;
BOOL success = [fileManager copyItemAtPath:sqlBundlePath toPath:sqlDocumentPath error:&error];
if (! success) {
return NO;
}
}
_dbPath = sqlDocumentPath;
return YES;
}
+ (BOOL)connect {
sqlite3_config(SQLITE_CONFIG_SERIALIZED);
return sqlite3_open([_dbPath UTF8String], &_db);
}
#pragma mark - Querying
- (BOOL)prepare:(NSString *)sql withBindings:(NSArray *)bindings {
// ERROR CAN OCCUR ON THE FOLLOWING LINE
if (! sqlite3_prepare_v2(_db, [sql UTF8String], -1, &_statement, NULL) == SQLITE_OK) {
NSLog(#"Error whilst preparing query: %s", sqlite3_errmsg(_db));
sqlite3_finalize(_statement);
return NO;
}
for (int i = 0; i < [bindings count]; i++) {
sqlite3_bind_text(_statement,
i + 1,
[bindings[i] isKindOfClass:[NSNull class]] ? [#"" UTF8String] : [bindings[i] UTF8String],
-1,
SQLITE_TRANSIENT);
}
return YES;
}
- (BOOL)stepThrough {
// ERROR CAN OCCUR ON THE FOLLOWING LINE
if (sqlite3_step(_statement) == SQLITE_ROW) {
return YES;
}
sqlite3_finalize(_statement);
return NO;
}
- (void)finalise {
sqlite3_finalize(_statement);
}
#end
Upgrading to the new version of the SDK (3.13) fixed this issue (at least for me), even though their changelog does not specifically mention it.

out of memory when reinstalling aplication

I'm desperate.
I create an application using sqlite an it was working fine until I submit an update to the apple store.
when my application is reinstalled and I tried to open a db connection I got an "out of memory" issue.
I've tried lots of stuff to solve this issue, but until today I was unable to solve this.
I use the code below to open db's connection:
-(Boolean)openConectionSQLiteDb
{
if (sqlite3_open([[GeneralFunction getInfoFromUserDefault:#"dbpath"] UTF8String], &db) != SQLITE_OK)
{
NSLog(#"Failed to open database!");
NSLog(#"%s",sqlite3_errmsg(statement));
//[GeneralFunction setDb:[[DbInteraction alloc] init:[GeneralFunction getDbName]]];
{
NSLog(#"Failed to open database! >>%#",[GeneralFunction getInfoFromUserDefault:#"dbpath"]);
// NSLog(#"222%s",sqlite3_errmsg(statement));
}
sqlite3_finalize(statement);
return false;
}
else
{
sqlite3_exec(db, "PRAGMA foreign_keys = on", NULL, NULL, NULL);
return true;
}
return false;
}
And the code below is used to insert date on the database:
-(Boolean)insertOrReplace:(NSDictionary *)elementoToInsert inTable:(NSString *)tableName
{
if([self openConectionSQLiteDb])
{
NSString * insert = [self createInsertStringWith:elementoToInsert in:tableName];
insert = [insert stringByReplacingOccurrencesOfString:#"INSERT" withString:#"INSERT OR REPLACE "];
const char *insert_stmt = [insert UTF8String];
sqlite3_prepare_v2(db, insert_stmt,
-1, &statement, NULL);
if (sqlite3_step(statement) == SQLITE_DONE)
{
sqlite3_finalize(statement);
sqlite3_close(db);
// NSLog(#"FECHOU");
return true;
}
NSLog(#"INSERT ERROR %s",sqlite3_errmsg(db));
sqlite3_finalize(statement);
}
sqlite3_close(db);
NSLog(#"FECHOU");
return false;
}
I'm also use this code to copy the Db file to a writable path when it necessary:
-(id)init:(NSString *) dbName
{
self = [super init];
nomeDb = dbName;
BOOL success;
NSFileManager *filemngr = [[NSFileManager alloc] init];
NSError *error;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSLibraryDirectory, NSUserDomainMask, YES);
NSString *documentDirectory = [paths objectAtIndex:0];
NSString *writableDbPath = [documentDirectory stringByAppendingPathComponent:nomeDb];
// NSLog(#"%#",[GeneralFunction getInfoFromUserDefault:#"dbpath"]);
NSString * lastPath =[GeneralFunction getInfoFromUserDefault:#"dbpath"];
success = [filemngr fileExistsAtPath:lastPath];
if(!success)
{
NSLog(#"Arquivo não existe nesse Directorio >%#",lastPath);
success = [filemngr fileExistsAtPath:writableDbPath];
if(!success)
{
NSLog(#"Arquivo não existe nesse Directorio >%#",writableDbPath);
NSString *defaultDbPath = [[[NSBundle mainBundle] resourcePath]stringByAppendingPathComponent:nomeDb];
success = [filemngr copyItemAtPath:defaultDbPath toPath:writableDbPath error:&error];
NSLog(#"Copiou Para :%#",writableDbPath);
if (!success)
{
NSLog(#"Arquivo não existe nesse Directorio pela seguanda vez >%#",writableDbPath);
//[status setText:[error localizedDescription]];
/* UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error!"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];*/
}
else
{
NSLog(#"Achou 2");
NSMutableDictionary * dic = [[NSMutableDictionary alloc] init];
// NSLog(#"%#",writableDbPath);
[dic setObject:writableDbPath forKey:#"dbpath"];
[GeneralFunction saveTeacherInfo:dic];
writablePath = writableDbPath;
}
}
else
{
NSLog(#"Banco esta em %#",writableDbPath);
}
}
else{
NSLog(#"Achou");
}
// [bd executeUpdate:#"PRAGMA foreign_keys=ON"];
return self;
}
So when I install the application for the first time, it works fine. But if I run the applications again (It happens when I submit an update too) I got the LOG error "Failed to open database out of memory".
Someone please help me, I'm almost getting fired.
PS: I'm sorry for my terrible English. ;D
sqlite reports "out of memory" errors incorrectly at times. Usually if this occurs after an app update, it's because you have a bug in your sqlite access code. If your database schema has changed between app versions, but you didn't make sure that your database calls are backwards-compatible, you will frequently see this error.
If your CREATE TABLE statements aren't followed by ALTER TABLE statements to bring your schema up to speed when the CREATE fails because the table already exists, you run the risk of making database calls that are not backwards-compatible.
YEAH GUYS, finally I could solve my issue.
It was happening because when I was checking if there was a Database already copied in my writable path, in the TRUE condition I forgot to update my internal path that refers to the DB. For that reason when I was trying to open a connection, it was returning me the error because I was looking for the database in wrong place.

Can't open SQLite database due out of memory error

I'm having an issue opening my sqlite database for an iPhone app I'm writing. I thought I followed the tutorials verbatim but for some reason I am getting an "Out of memory" error.
-(NSString *) filepath{
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentationDirectory, NSUserDomainMask, YES);
return [[paths objectAtIndex:0] stringByAppendingPathComponent:#"db.sqlite"];
}
-(sqlite3*)openDB{
if(db == NULL){
sqlite3 *newDBConnection;
if(sqlite3_open([[self filepath] UTF8String], &newDBConnection) != SQLITE_OK){
sqlite3_close(db);
NSLog(#"%s SQL error '%s' (%1d)", __FUNCTION__, sqlite3_errmsg(db), sqlite3_errcode(db));
db = NULL;
}
else{
NSLog(#"db opened");
}
}
return db;
}
DB is an ivar and I am calling db = [self openDB]; in the initialization method.
The sqlite3_open is failing because you are using NSDocumentationDirectory instead of NSDocumentDirectory.
The reason you're receiving the "Out of memory" error is that sqlite3_open is updating newDBConnection, but the sqlite3_errmsg is trying to use db (which is still NULL). And whenever you call sqlite3_errmsg with a NULL for the sqlite3 pointer, SQLite somewhat confusingly returns an "Out of memory" message.
Also note that even if you fix the two above issues, note that you are calling sqlite3_errmsg after performing sqlite3_close. Make sure to get your error message before you call sqlite3_close.
E.g. I would suggest:
- (BOOL)openDB {
if (db == NULL) {
int rc;
if ((rc = sqlite3_open([[self filepath] UTF8String], &db)) != SQLITE_OK) {
NSLog(#"%s SQL error '%s' (%d)", __FUNCTION__, sqlite3_errmsg(db), rc);
sqlite3_close(db);
db = NULL;
return false; // open failed
} else {
NSLog(#"db opened");
return true; // open successful
}
}
return true; // already open
}
Note, a minor point, given that sqlite3_open returns the error code, I would just save that directly, rather than calling sqlite3_errcode to get the code that was just returned.

Running an iOS SQLite Updates

I'm using this to do an Update statement in a test iOS App, the log outputs 'Success' but theres no change in the database. Could anyone point me in the right direction? It'd be greatly appreciated.
I'm brand new to this so please forgive my probably appallingly written code.
#try {
NSFileManager *fileMgr = [NSFileManager defaultManager];
NSString *dbPath = [[[NSBundle mainBundle] resourcePath ]stringByAppendingPathComponent:#"chars.sqlite3"];
BOOL success = [fileMgr fileExistsAtPath:dbPath];
if(!success)
{
NSLog(#"Cannot locate database file '%#'.", dbPath);
}
if(!(sqlite3_open([dbPath UTF8String], &db) == SQLITE_OK))
{
NSLog(#"An error has occured: %s", sqlite3_errmsg(db));
}
const char *sql = "UPDATE characters SET level = 'testing' WHERE id='1'";
sqlite3_stmt *sqlStatement;
if(sqlite3_prepare(db, sql, -1, &sqlStatement, NULL) != SQLITE_OK)
{
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}else{
if (sqlite3_step(sqlStatement) == SQLITE_DONE){
NSLog(#"Success");
}
}
sqlite3_finalize(sqlStatement);
}
#catch (NSException *exception) {
NSLog(#"Problem with prepare statement: %s", sqlite3_errmsg(db));
}
#finally {
sqlite3_close(db);
}
The problem is that you're opening the database from the bundle, which is read-only. You should
Check for the existence of the database in the Documents folder.
If not there, copy the database from the bundle to the Documents folder.
Now, open the database from the Documents folder.

Sqlite in iOS memory issues

I spent hours trying to fix that but I've just given up; I have no idea what's wrong.
In my app, I do nested SQL operations to set all my objects correctly. For some reason, sometimes the sqlite3 objects do not get release properly, causing the memory to go up the roof. I understand it is a problem with using correctly sql3_close and sql3_finalize. However, as you will see, I think I have used them correctly.
Here is the source of the problem:
- (NSArray *) getAllSessions {
if (sqlite3_open(dbPath, &db) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
//I found out that doing something like that
//toAdd.in_loc = [self getIndoorLocationWithId:[NSNumber numberWithInt:(int)sqlite3_column_int(statement, 6)]];
//messes the memory up all the time
//but doing that works OK:
NSNumber *_id = [[NSNumber alloc] initWithInt:(int) sqlite3_column_int(statement, 5)];
toAdd.out_loc = [self getOutdoorLocationWithId:_id];
[_id release];
//So I did the same with the second one, but this one messes memory up:
NSNumber *id2 = [[NSNumber alloc] initWithInt:(int)sqlite3_column_int(statement, 6)];
toAdd.in_loc = [self getIndoorLocationWithId:id2];
[id2 release];
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
}
}
So here is the one that messes memory up:
- (IndoorLocation *) getIndoorLocationWithId:(NSNumber *) locId {
if (sqlite3_open([databasePath UTF8String], &db) == SQLITE_OK) {
if (sqlite3_prepare_v2(db, query_stmt, -1, &statement, NULL) == SQLITE_OK) {
while (sqlite3_step(statement) == SQLITE_ROW) {
//if I comment the thing below it works
NSNumber *_id = [[NSNumber alloc] initWithInt:(int) sqlite3_column_int(statement, 5)];
toReturn.outLoc = [self getOutdoorLocationWithId:_id];
[_id release];
}
sqlite3_finalize(statement);
}
sqlite3_close(db);
}
}
So in the one that messes memory up, I use exactly the same function as the first time (getOutdoorLocationwithId), in the same way but it doesn't work, sqlite3 objects don't get released properly.
I hope you understand my problem, this is driving me nuts!
In a single user app like this, there's no need to continually open and close. This is not like a server based app where you have connection pooling with multiple users where holding the connection can defeat the pool and hurt scalability. In a phone app open it and hold it open. close it when you're done. At a minimum, do that within a recursive call.
To make it worse, you're opening and closing within recursive calls - just leave it open.
Also:
You're not checking the return codes of finalize
You're not checking the return codes of close.
You're preparing (compiling) the statments but you're not saving them off - call reset on the saved statement and execute it again.
Consider using FMDB - it's a good wrapper.
BTW, here's a richer and more durable close but don't use it on every call. Close it when you're ending or your app is going into the background ... This is mine which is similar to what fmdb does.
- (void)close
{
if (_sqlite3)
{
ENInfo(#"closing");
[self clearStatementCache];
int rc = sqlite3_close(_sqlite3);
ENDebug(#"close rc=%d", rc);
if (rc == SQLITE_BUSY)
{
ENError(#"SQLITE_BUSY: not all statements cleanly finalized");
sqlite3_stmt *stmt;
while ((stmt = sqlite3_next_stmt(_sqlite3, 0x00)) != 0)
{
ENDebug(#"finalizing stmt");
sqlite3_finalize(stmt);
}
rc = sqlite3_close(_sqlite3);
}
if (rc != SQLITE_OK)
{
ENError(#"close not OK. rc=%d", rc);
}
_sqlite3 = NULL;
}
}
I met the same issue.
When i use and open FMDB, it works OK. but in other callbacks it failed and threw exception of "Closing leaked statement." Finally I found that's because i kept the pointer from [FMDatabase databaseWithPath:dbPath] and the pointer is a autorelease object. I fixed the issue by this:
FMDatabase *db = [[FMDatabase databaseWithPath:dbPath] retain];
and when you want to close the database:
db [close];
[db release];
db = nil;
By this, you don't always open and close database for each operation, the should be a manager object accounting for it. When the manager starts, the database is open always until the manager stops.

Resources