Sqlcipher backend

  • Although this extention’s code is short, it has not been properlypeer-reviewed yet and may have introduced vulnerabilities.

Also note that this code relies on pysqlcipher and sqlcipher, andthe code there might have vulnerabilities as well, but since theseare widely used crypto modules, we can expect “short zero days” there.

sqlcipher_ext API notes

  • class SqlCipherDatabase(database, passphrase, **kwargs)
  • Subclass of SqliteDatabase that stores the databaseencrypted. Instead of the standard sqlite3 backend, it uses pysqlcipher:a python wrapper for sqlcipher, which – in turn – is an encrypted wrapperaround sqlite3, so the API is identical to SqliteDatabase’s,except for object construction parameters:

Parameters:

  • database – Path to encrypted database filename to open [or create].
  • passphrase – Database encryption passphrase: should be at least 8 characterlong, but it is strongly advised to enforce better passphrase strengthcriteria in your implementation.
  • If the database file doesn’t exist, it will be created withencryption by a key derived from passhprase.
  • When trying to open an existing database, passhprase should beidentical to the ones used when it was created. If the passphrase isincorrect, an error will be raised when first attempting to access thedatabase.
  • rekey(passphrase)

Parameters:passphrase (str) – New passphrase for database.

Change the passphrase for database.

Note

SQLCipher can be configured using a number of extension PRAGMAs. The listof PRAGMAs and their descriptions can be found in the SQLCipher documentation.

For example to specify the number of PBKDF2 iterations for the keyderivation (64K in SQLCipher 3.x, 256K in SQLCipher 4.x by default):

  1. # Use 1,000,000 iterations.
  2. db = SqlCipherDatabase('my_app.db', pragmas={'kdf_iter': 1000000})

To use a cipher page-size of 16KB and a cache-size of 10,000 pages:

  1. db = SqlCipherDatabase('my_app.db', passphrase='secret!!!', pragmas={
  2. 'cipher_page_size': 1024 * 16,
  3. 'cache_size': 10000}) # 10,000 16KB pages, or 160MB.

Example of prompting the user for a passphrase:

  1. db = SqlCipherDatabase(None)
  2.  
  3. class BaseModel(Model):
  4. """Parent for all app's models"""
  5. class Meta:
  6. # We won't have a valid db until user enters passhrase.
  7. database = db
  8.  
  9. # Derive our model subclasses
  10. class Person(BaseModel):
  11. name = TextField(primary_key=True)
  12.  
  13. right_passphrase = False
  14. while not right_passphrase:
  15. db.init(
  16. 'testsqlcipher.db',
  17. passphrase=get_passphrase_from_user())
  18.  
  19. try: # Actually execute a query against the db to test passphrase.
  20. db.get_tables()
  21. except DatabaseError as exc:
  22. # This error indicates the password was wrong.
  23. if exc.args[0] == 'file is encrypted or is not a database':
  24. tell_user_the_passphrase_was_wrong()
  25. db.init(None) # Reset the db.
  26. else:
  27. raise exc
  28. else:
  29. # The password was correct.
  30. right_passphrase = True

See also: a slightly more elaborate example.