From b48f000e311daf543a8b8f776c5438725485bffd Mon Sep 17 00:00:00 2001 From: drebs Date: Tue, 23 Apr 2013 10:22:05 -0300 Subject: Separate crypto-related stuff from Soledad class. This creates a SoledadCrypto object that should encapsulate everything related to crypto in Soledad. Also, replace hmac for sha256 when creating hashes. --- src/leap/soledad/backends/leap_backend.py | 41 +++++++++++++++++-------------- src/leap/soledad/backends/sqlcipher.py | 35 +++++++++++++------------- 2 files changed, 40 insertions(+), 36 deletions(-) (limited to 'src/leap/soledad/backends') diff --git a/src/leap/soledad/backends/leap_backend.py b/src/leap/soledad/backends/leap_backend.py index 4ea131c0..687b59ef 100644 --- a/src/leap/soledad/backends/leap_backend.py +++ b/src/leap/soledad/backends/leap_backend.py @@ -42,7 +42,7 @@ class NoDefaultKey(Exception): pass -class NoSoledadInstance(Exception): +class NoSoledadCryptoInstance(Exception): """ Exception to signal that no Soledad instance was found. """ @@ -69,7 +69,7 @@ class LeapDocument(Document): """ def __init__(self, doc_id=None, rev=None, json='{}', has_conflicts=False, - encrypted_json=None, soledad=None, syncable=True): + encrypted_json=None, crypto=None, syncable=True): """ Container for handling an encryptable document. @@ -84,14 +84,14 @@ class LeapDocument(Document): @param encrypted_json: The encrypted JSON string for this document. If given, the decrypted value supersedes any raw json string given. @type encrypted_json: str - @param soledad: An instance of Soledad so we can encrypt/decrypt + @param crypto: An instance of SoledadCrypto so we can encrypt/decrypt document contents when syncing. - @type soledad: soledad.Soledad + @type crypto: soledad.Soledad @param syncable: Should this document be synced with remote replicas? @type syncable: bool """ Document.__init__(self, doc_id, rev, json, has_conflicts) - self._soledad = soledad + self._crypto = crypto self._syncable = syncable if encrypted_json: self.set_encrypted_json(encrypted_json) @@ -103,17 +103,20 @@ class LeapDocument(Document): @return: The encrypted JSON serialization of document's contents. @rtype: str """ - if not self._soledad: - raise NoSoledadInstance() - return self._soledad.encrypt_symmetric(self.doc_id, - self.get_json()) + if not self._crypto: + raise NoSoledadCryptoInstance() + return self._crypto.encrypt_symmetric( + self.get_json(), + self._crypto._hash_passphrase(self.doc_id)) def set_encrypted_content(self, cyphertext): """ Decrypt C{cyphertext} and set document's content. contents. """ - plaintext = self._soledad.decrypt_symmetric(self.doc_id, cyphertext) + plaintext = self._crypto.decrypt_symmetric( + cyphertext, + self._crypto._hash_passphrase(self.doc_id)) self.set_json(plaintext) def get_encrypted_json(self): @@ -131,8 +134,8 @@ class LeapDocument(Document): Set document's content based on a valid JSON string containing the encrypted document's contents. """ - if not self._soledad: - raise NoSoledadInstance() + if not self._crypto: + raise NoSoledadCryptoInstance() cyphertext = json.loads(encrypted_json)['_encrypted_json'] self.set_encrypted_content(cyphertext) @@ -196,7 +199,7 @@ class LeapSyncTarget(HTTPSyncTarget): receiving. """ - def __init__(self, url, creds=None, soledad=None): + def __init__(self, url, creds=None, crypto=None): """ Initialize the LeapSyncTarget. @@ -210,7 +213,7 @@ class LeapSyncTarget(HTTPSyncTarget): @type soledad: soledad.Soledad """ HTTPSyncTarget.__init__(self, url, creds) - self._soledad = soledad + self._crypto = crypto def _parse_sync_stream(self, data, return_doc_cb, ensure_callback=None): """ @@ -244,15 +247,15 @@ class LeapSyncTarget(HTTPSyncTarget): line, comma = utils.check_and_strip_comma(entry) entry = json.loads(line) # decrypt after receiving from server. - if not self._soledad: - raise NoSoledadInstance() + if not self._crypto: + raise NoSoledadCryptoInstance() enc_json = json.loads(entry['content'])['_encrypted_json'] - if not self._soledad.is_encrypted_sym(enc_json): + if not self._crypto.is_encrypted_sym(enc_json): raise DocumentNotEncrypted( "Incoming document from sync is not encrypted.") doc = LeapDocument(entry['id'], entry['rev'], encrypted_json=entry['content'], - soledad=self._soledad) + crypto=self._crypto) return_doc_cb(doc, entry['gen'], entry['trans_id']) if parts[-1] != ']': try: @@ -300,7 +303,7 @@ class LeapSyncTarget(HTTPSyncTarget): # encrypt and verify before sending to server. enc_json = json.loads( doc.get_encrypted_json())['_encrypted_json'] - if not self._soledad.is_encrypted_sym(enc_json): + if not self._crypto.is_encrypted_sym(enc_json): raise DocumentNotEncrypted( "Could not encrypt document before sync.") size += prepare(id=doc.doc_id, rev=doc.rev, diff --git a/src/leap/soledad/backends/sqlcipher.py b/src/leap/soledad/backends/sqlcipher.py index a4d53fa8..288680d4 100644 --- a/src/leap/soledad/backends/sqlcipher.py +++ b/src/leap/soledad/backends/sqlcipher.py @@ -38,7 +38,7 @@ from u1db import ( from leap.soledad.backends.leap_backend import LeapDocument -def open(path, password, create=True, document_factory=None, soledad=None): +def open(path, password, create=True, document_factory=None, crypto=None): """Open a database at the given location. Will raise u1db.errors.DatabaseDoesNotExist if create=False and the @@ -58,7 +58,7 @@ def open(path, password, create=True, document_factory=None, soledad=None): """ return SQLCipherDatabase.open_database( path, password, create=create, document_factory=document_factory, - soledad=soledad) + crypto=crypto) class DatabaseIsNotEncrypted(Exception): @@ -78,7 +78,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): db_handle.cursor().execute("PRAGMA key = '%s'" % key) def __init__(self, sqlcipher_file, password, document_factory=None, - soledad=None): + crypto=None): """ Create a new sqlcipher file. @@ -89,23 +89,24 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @param document_factory: A function that will be called with the same parameters as Document.__init__. @type document_factory: callable - @param soledad: An instance of Soledad so we can encrypt/decrypt + @param crypto: An instance of SoledadCrypto so we can encrypt/decrypt document contents when syncing. - @type soledad: soledad.Soledad + @type crypto: soledad.crypto.SoledadCrypto """ self._check_if_db_is_encrypted(sqlcipher_file) self._db_handle = dbapi2.connect(sqlcipher_file) SQLCipherDatabase.set_pragma_key(self._db_handle, password) self._real_replica_uid = None self._ensure_schema() - self._soledad = soledad + self._crypto = crypto def factory(doc_id=None, rev=None, json='{}', has_conflicts=False, encrypted_json=None, syncable=True): return LeapDocument(doc_id=doc_id, rev=rev, json=json, has_conflicts=has_conflicts, encrypted_json=encrypted_json, - syncable=syncable, soledad=self._soledad) + syncable=syncable, + crypto=self._crypto) self.set_document_factory(factory) def _check_if_db_is_encrypted(self, sqlcipher_file): @@ -131,7 +132,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @classmethod def _open_database(cls, sqlcipher_file, password, document_factory=None, - soledad=None): + crypto=None): """ Open a SQLCipher database. @@ -142,9 +143,9 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @param document_factory: A function that will be called with the same parameters as Document.__init__. @type document_factory: callable - @param soledad: An instance of Soledad so we can encrypt/decrypt + @param crypto: An instance of SoledadCrypto so we can encrypt/decrypt document contents when syncing. - @type soledad: soledad.Soledad + @type crypto: soledad.crypto.SoledadCrypto @return: The database object. @rtype: SQLCipherDatabase @@ -171,11 +172,11 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): time.sleep(cls.WAIT_FOR_PARALLEL_INIT_HALF_INTERVAL) return SQLCipherDatabase._sqlite_registry[v]( sqlcipher_file, password, document_factory=document_factory, - soledad=soledad) + crypto=crypto) @classmethod def open_database(cls, sqlcipher_file, password, create, backend_cls=None, - document_factory=None, soledad=None): + document_factory=None, crypto=None): """ Open a SQLCipher database. @@ -191,9 +192,9 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): @param document_factory: A function that will be called with the same parameters as Document.__init__. @type document_factory: callable - @param soledad: An instance of Soledad so we can encrypt/decrypt + @param crypto: An instance of SoledadCrypto so we can encrypt/decrypt document contents when syncing. - @type soledad: soledad.Soledad + @type crypto: soledad.crypto.SoledadCrypto @return: The database object. @rtype: SQLCipherDatabase @@ -201,7 +202,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): try: return cls._open_database(sqlcipher_file, password, document_factory=document_factory, - soledad=soledad) + crypto=crypto) except errors.DatabaseDoesNotExist: if not create: raise @@ -211,7 +212,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): backend_cls = SQLCipherDatabase return backend_cls(sqlcipher_file, password, document_factory=document_factory, - soledad=soledad) + crypto=crypto) def sync(self, url, creds=None, autocreate=True): """ @@ -234,7 +235,7 @@ class SQLCipherDatabase(sqlite_backend.SQLitePartialExpandDatabase): self, LeapSyncTarget(url, creds=creds, - soledad=self._soledad)).sync(autocreate=autocreate) + crypto=self._crypto)).sync(autocreate=autocreate) def _extra_schema_init(self, c): """ -- cgit v1.2.3