Yeah, I see the same two solutions. The first solution would also ensure 
currently encrypted databases will still be working. Whereas changing the 
encryption would make live a lot harder for currently deployed databases, which 
would need a conversion.

For testing purposes I just changed the encryption algorithm in 
System.Data.SQLite to exclude the change counter, so the change counter remains 
plaintext. My tests with the 3 threads did run fine and without any problems 
for several runs I've executed.

It would be great if there's a statement from the SQLite developers what the 
requirements for the encryptor are. And what their opinion is regarding this 
issue.

> -----Ursprüngliche Nachricht-----
> Von: sqlite-users-boun...@sqlite.org [mailto:sqlite-users-
> boun...@sqlite.org] Im Auftrag von Michael Stephenson
> Gesendet: Freitag, 16. Dezember 2011 15:27
> An: 'General Discussion of SQLite Database'
> Betreff: Re: [sqlite] SQLite reading old data
> 
> I think the only solutions would be:  1) SQLIte changes so that it does not 
> use
> a direct file read/write for the change counter.  2) Have the page-level
> encryptor specifically not encrypt certain parts of the database header, such
> as the change counter, when saving page 1 to disk.
> 
> I imagine that the direct reads/writes are to help ensure consistency and
> recovery in the face of an application crash.
> 
> You might try turning on WAL and see what happens.
> 
> -----Original Message-----
> From: sqlite-users-boun...@sqlite.org
> [mailto:sqlite-users-boun...@sqlite.org] On Behalf Of Fabrizio Steiner
> Sent: Thursday, December 15, 2011 3:00 PM
> To: General Discussion of SQLite Database
> Subject: Re: [sqlite] SQLite reading old data
> 
> The answers to your questions :
> 1)Yes I'm still using System.Data.SQLite but without any key.
> 2)No I don't use WAL.
> 3)Running only one thread didn't bring up the problem.
> 4)Didn't have any time yet.
> 5)Standard System.Data.SQLite with RC4 algorithm impleemnted there.
> 
> I've taken a look into the the caching of SQLite during the afternoon because
> I suspected some issues with caching. And I found a problem with the change
> counter exactly as you thought.
> 
> I took a look into the source code of the pager and the pager cache. I've
> found the pager cache decides if the cache needs to be flushed whenever a
> shared lock gets requested. In order to determine if a flush is needed or not,
> the change counter of the db file is compared to the stored one in the pager
> structure. The change counter is directly retrieved from the underlying OS
> (sqlite3OsRead), so if the database is encrypted, the encrypted bytes will be
> retrieved here (Sytem.Data.SQLite encrypts the entire database, including
> page 1 and the SQLite header).
> 
> So dbFileVers of the pager structure is always the encrypted value. I started
> checking if there was a situation where it was used as the plaintext value.
> 
> In the function pager_write_changecounter the change counter will get
> incremented, but the encrypted value pPg->pPager->dbFileVer is used.
> After incrementation the value will be put back into the page buffer. During
> the write of the page 1, the is again encrypted (see, pager_write_pagelist).
> After the page has been written the dbFileVers gets updated
> (pager_write_pagelist Line 4049) with the encrypted value.
> 
> So at least for incrementing the change counter the plaintext value should be
> used.
> 
> When RC4 with the full database encryption (as implemented in
> System.Data.SQLite) is used the following happens.
> RC4 basically XORs a plaintext byte with a pseudo random byte. Let's assume
> the following change counter values, for simplicity consider only the last 4
> bits of it. As we've seen the encrypted change counter is incremented,
> encrypted and stored back into the file. Let's consider the following
> operations.
> 
> DB Initial State
> - Encrypted DB File counter:      X                   Y                   Z
> 0    (The bits X, Y, Z are unknown, but the last bit is 0.)
> 
> First update of Database:
> - Update DB, Increment counter:   X                   Y                   Z
> 1    (Adding one to the encrypted counter.)
> - New encrypted Value in DB:     (X XOR K1)          (Y XOR K2)          (Z
> XOR K3)     (1 XOR 1) = 0    (Assuming the LSB of the pseudo byte is 1, the
> probability is 1/2 for this if its purely random.)
> 
> Let's update the database again:
> - Update DB, Increment counter:  (X XOR K1)          (Y XOR K2)          (Z
> XOR K3)         1    (Adding one to the encrypted counter.)
> - New encrypted Value in DB:    ((X XOR K1) XOR K1) ((Y XOR K2) XOR K2) ((Z
> XOR K3) XOR K3) 0    (The same pseudo byte is again used for encryption.)
>                                =  X                   Y                   Z
> 0
> 
> As a result after the second db update the encrypted change counter is the
> same as before the updates occured. dbFileVers contains 12 more bytes but
> these represent the "db size in pages", the "page number of the first free
> page" and the "number of free pages". But these may be unchanged if no
> new pages were needed and there was no free page.
> 
> => Therefore theres a good chance that a db change is undetected
> depending on the encryption algorithm.
> 
> Is it allowed to encrypt the entire databse especially the header with the
> pagesize, change counter and so on?
>    - If yes, then SQLite should make sure all data get's decrypted prior using
> these values.
>    - If not, shouldn't SQLite make sure the crypt api never sees the header?
> 
> I've found no documentation about implementing the crypting api.
> 
> Kind Regards
> Fabrizio
> 
> > -----Ursprüngliche Nachricht-----
> > Von: sqlite-users-boun...@sqlite.org [mailto:sqlite-users-
> > boun...@sqlite.org] Im Auftrag von Michael Stephenson
> > Gesendet: Donnerstag, 15. Dezember 2011 20:40
> > An: 'General Discussion of SQLite Database'
> > Betreff: Re: [sqlite] SQLite reading old data
> >
> > A couple more questions:
> >
> > 1)  Does "without encryption" mean still using System.Data.SQLite,
> > just without using a key?
> > 2)  Are you using WAL?
> > 3)  Do you also see the problem with a single process with two
> connections?
> > 4)  Any chance you could provide a small demo project that
> > demonstrates the problem?
> > 5)  Are you using standard System.Data.SQLite, or the SqlCipher version?
> > (http://sqlcipher.net)
> >
> > I'm pretty fuzzy on this, but here are some (possibly way off base)
> > thoughts...
> >
> > The way I understand things, when you do an update inside of a
> > transaction, the new data is written to the log until it is committed,
> > and then a rollback involves discarding the log page with the changes.
> > This means you expect to see the new data in your "session" but no one
> > else should see the new data until you commit.
> >
> > The question of which version of the data a session sees is, I think,
> > tied to some global counter-type metadata that tracks changes.  In
> > Oracle, this would be perhaps a system change number that would
> > determine which undo blocks your session would see versus other
> > sessions.  I think SQLite has something similar, and I think this is
> > stored in the database header on the first database page.
> >
> > I also recall that there are a few places in SQLite where it reads
> > data for page
> > 1 using system-level I/O rather than the pager (e.g., direct reads on
> > the database file), and this causes issues for page-level encryptors
> > because they don't get an opportunity to decrypt the encrypted page
> > before SQLite reads that piece of data, so they typically have to
> > hard-code some value at that data location or hope that SQLite ignores
> > what would be an obviously invalid value.  I think the change counter
> > is
> one of these items.
> >
> > Maybe folks with much more understanding could make sense of this, but
> > my thought is that the equivalent of the "system change number" is
> > being mucked up by the page-level encryptor, causing SQLite to get the
> > old data because as far as it knows that's the data at the given
> > (invalid)
> change count.
> >
> > Well, something like that.  Again, probably way off base, but I had a
> > couple of minutes and thought I'd throw it out there...
> >
> > ~Mike
> >
> > -----Original Message-----
> > From:
> > sqlite-users-boun...@sqlite.org<mailto:sqlite-users-boun...@sqlite.org
> > >
> > [mailto:sqlite-users-boun...@sqlite.org]<mailto:[mailto:sqlite-users-b
> > oun...@sqlite.org]> On Behalf Of Fabrizio Steiner
> > Sent: Thursday, December 15, 2011 9:01 AM
> > To: General Discussion of SQLite Database
> > Subject: Re: [sqlite] SQLite reading old data
> >
> > I think the following observation, which I made in the meantime, may
> > be very interesting. Which also gives a hint to a problem with SQLite
> > in combination with the encryption of System.Data.SQLite.
> >
> > I tried the same test with an unencrypted database, and surprisingly
> > the 3 threads do their work without any exception. I executed several
> > test runs with the 3 threads and it wasn't reproducable anymore.
> > Activating the encryption again it's reproducable and exceptions occur
> > due to an old stamp that is being retrieved.
> >
> > Answers to your questions:
> > - No connections are shared at all between threads. So every thread
> > creates
> > 2 private connections.
> > - Updating always occurs in the second connection.
> > - The after-update checks will be perfomed in the same transaction as
> > the update itself.
> > - No thread ever deletes a row.
> >
> > Regards
> > Fabrizio
> >
> > > -----Ursprüngliche Nachricht-----
> > > Von:
> > > sqlite-users-boun...@sqlite.org<mailto:sqlite-users-bounces@sqlite.o
> > > rg> [mailto:sqlite-users-
> > > boun...@sqlite.org<mailto:boun...@sqlite.org>] Im Auftrag von Igor
> > > Tandetnik
> > > Gesendet: Donnerstag, 15. Dezember 2011 14:26
> > > An: sqlite-users@sqlite.org<mailto:sqlite-users@sqlite.org>
> > > Betreff: Re: [sqlite] SQLite reading old data
> > >
> > > Fabrizio Steiner <f...@bbtsoftware.ch<mailto:f...@bbtsoftware.ch>>
> wrote:
> > > > I'm currently facing a problem with the System.Data.SQLite ADO.NET
> > > > Provider. I'm using stamps (simple integer column) on the rows in
> > > > order to detect if another user has altered the same datarow
> > > > during the
> > > time the first user started the edit and when he invoked the save.
> > > Every update increases this stamp and the update statement is
> > > constrained with the old stamp. If the update has
> > > > no affected rows, there are different cases which are checked:
> > > > - If the datarow has been deleted, the user is informed about this.
> > > > - If the datarow still exists the current stamp in the database is
> > retrieved.
> > > >   - If the db stamp is greater than the reference stamp, the user
> > > > will be informed that another user changed the data in the meantime.
> > > >   - If the db stamp is smaller or equal to the reference stamp, an
> > > > exception
> > > is thrown because the stamp can never get smaller.
> > > >
> > > > I tried to simulate 3 users which are updating a datarow in the
> > > > same table, every user changes it's own row.  Basically I had 3
> > > > threads each of one first retrieving a row in one connection.
> > > > Updating the data and
> > > storing it back into the table with another connection. Every thread
> > > made a loop with several edits and a sleep in between.
> > >
> > > Do you have two connections, each shared by three threads; or six
> > > separate connections? Do you ever perform any writing on the
> > > connection that reads the original version of the row? Do you
> > > perform after-update checks on the same connection as the update
> > > itself? Do you wrap the update and the checks together in a transaction?
> > >
> > > How is a particular "datarow" identified? Is it possible for one
> > > thread to delete a row, and then for another to insert it back later
> > > as new (presumably with the version stamp starting back from zero)?
> > > --
> > > Igor Tandetnik
> > >
> > > _______________________________________________
> > > sqlite-users mailing list
> > > sqlite-users@sqlite.org<mailto:sqlite-users@sqlite.org>
> > > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
> > _______________________________________________
> > sqlite-users mailing list
> > sqlite-users@sqlite.org<mailto:sqlite-users@sqlite.org>
> > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
> >
> > _______________________________________________
> > sqlite-users mailing list
> > sqlite-users@sqlite.org<mailto:sqlite-users@sqlite.org>
> > http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
> 
> _______________________________________________
> sqlite-users mailing list
> sqlite-users@sqlite.org
> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
> 
> _______________________________________________
> sqlite-users mailing list
> sqlite-users@sqlite.org
> http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users
_______________________________________________
sqlite-users mailing list
sqlite-users@sqlite.org
http://sqlite.org:8080/cgi-bin/mailman/listinfo/sqlite-users

Reply via email to