If you are using System.Data.SQLite with the provided Encryption you may run 
into the same problem as I had.

http://www.mail-archive.com/sqlite-users%40sqlite.org/msg66194.html

As Richard already clarified, this encryption is not officially supported by 
the SQLite team and is only there for backward compatibility with older 
System.Data.SQLite versions.

I've patched the the encryption myself, attached you will find two patches, 
which you could apply to the System.Data.SQLite source. 

It fixes two problems.
1. The change_counter is never encrypted anymore, fixing the problem of reading 
old data. All other content of the database is encrypted and hasn't been 
changed from the original implementation.
2. Initialization of the crypt context has to be thread-safe. Otherwise opening 
the database in a multi-threaded environment may fail.

Hope this helps...

Kind Regards
Fabrizio

> -----Original Message-----
> From: [email protected] [mailto:sqlite-users-
> [email protected]] On Behalf Of Szomor Akos
> Sent: Donnerstag, 31. Oktober 2013 13:22
> To: [email protected]
> Subject: [sqlite] Multi thread access for encrypted database bug
> 
> Hi,
> 
> Issue is happening when encrypted database opened by two or more
> program same time.
> (Databases without encryption are working good.)
> 
> Data modification by the first program is shown by the second one only if it 
> is
> the first, third, 5th, 7th, etc. (odd) modification sequentially since the 
> query
> of the second one.
> After modifying two, four, six, etc. (even) data sequentially the result is 
> not
> available in second program. The select command by the second program will
> get a previous state of database.
> 
> I hope it was understandable.
> 
> _______________________________________________
> sqlite-users mailing list
> [email protected]
> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
Index: SQLite.Interop/src/win/crypt.c
===================================================================
--- SQLite.Interop/src/win/crypt.c      (revision 11148)
+++ SQLite.Interop/src/win/crypt.c      (revision 11149)
@@ -11,6 +11,9 @@
 // Extra padding before and after the cryptographic buffer
 #define CRYPT_OFFSET 8
 
+#define CHANGE_COUNTER_OFFSET 24
+#define CHANGE_COUNTER(page1) ((LPBYTE)page1) + CHANGE_COUNTER_OFFSET
+
 typedef struct _CRYPTBLOCK
 {
   Pager    *pPager;       // Pager this cryptblock belongs to
@@ -143,10 +146,16 @@
   DWORD dwPageSize;
   LPVOID pvTemp;

+  char changeCounter[4]; //Stored change counter if the first page gets 
en/decrypted.
+
   if (!pBlock) return data;
   if (pBlock->pvCrypt == NULL) return NULL; // This only happens if 
CreateCryptBlock() failed to make scratch space
 
+  if (nPageNum == 1){
+    //Never encrypt the change counter, as the encrypted value is used by 
SQLite.
+    CopyMemory(changeCounter, CHANGE_COUNTER(data), sizeof(changeCounter));
+  }
+
   switch(nMode)
   {
   case 0: // Undo a "case 7" journal file encryption
@@ -207,6 +216,10 @@
     break;
   }
 
+  if (nPageNum == 1){
+    CopyMemory(CHANGE_COUNTER(data), changeCounter, sizeof(changeCounter));
+  }
+
   return data;
 }
 
Index: SQLite.Interop/src/win/crypt.c
===================================================================
--- SQLite.Interop/src/win/crypt.c      (revision 11149)
+++ SQLite.Interop/src/win/crypt.c      (revision 12174)
@@ -24,6 +24,9 @@
   DWORD     dwCryptSize;  // Equal to or greater than dwPageSize.  If larger, 
pvCrypt is valid and this is its size
 } CRYPTBLOCK, *LPCRYPTBLOCK;
 
+static long crypt_isInit = 0;
+static long crypt_initLock = 0;
+
 HCRYPTPROV g_hProvider = 0; // Global instance of the cryptographic provider
 
 #define SQLITECRYPTERROR_PROVIDER "Cryptographic provider not available"
@@ -42,13 +45,24 @@
 // most platforms
 static BOOL InitializeProvider()
 {
-  if (g_hProvider) return TRUE;
+  if (crypt_isInit)
+    return g_hProvider;
 
-  if (!CryptAcquireContext(&g_hProvider, NULL, MS_ENHANCED_PROV, 
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
-  {
-    return FALSE;
+  // Accordingly to 
https://groups.google.com/forum/?fromgroups=#!topic/microsoft.public.security.crypto/bJOlErtZDdo
+  // CryptAcquireContext is generally unsafe, therefore make sure only one 
thread creates it.
+  if (InterlockedCompareExchange(&crypt_initLock, 1, 0) == 0) {
+    if (!CryptAcquireContext(&g_hProvider, NULL, MS_ENHANCED_PROV, 
PROV_RSA_FULL, CRYPT_VERIFYCONTEXT))
+    {
+      g_hProvider = 0;
+    }
+    crypt_isInit = 1;
+  } else {
+    while (!crypt_isInit) {
+      Sleep(1);
+    }
   }
-  return TRUE;
+
+  return g_hProvider;
 }
 
 // Create or update a cryptographic context for a pager.
_______________________________________________
sqlite-users mailing list
[email protected]
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to