diff options
Diffstat (limited to 'src')
| -rw-r--r-- | src/leap/soledad/__init__.py | 12 | ||||
| -rw-r--r-- | src/leap/soledad/auth.py | 4 | ||||
| -rw-r--r-- | src/leap/soledad/backends/leap_backend.py | 199 | ||||
| -rw-r--r-- | src/leap/soledad/crypto.py | 64 | ||||
| -rw-r--r-- | src/leap/soledad/server.py | 1 | ||||
| -rw-r--r-- | src/leap/soledad/tests/__init__.py | 18 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_crypto.py | 75 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_leap_backend.py | 64 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_soledad.py | 3 | ||||
| -rw-r--r-- | src/leap/soledad/tests/test_sqlcipher.py | 10 | ||||
| -rw-r--r-- | src/leap/soledad/tests/u1db_tests/test_https.py | 3 | 
11 files changed, 312 insertions, 141 deletions
| diff --git a/src/leap/soledad/__init__.py b/src/leap/soledad/__init__.py index c70fee91..b051a80c 100644 --- a/src/leap/soledad/__init__.py +++ b/src/leap/soledad/__init__.py @@ -374,7 +374,6 @@ class Soledad(object):          This method will also replace the secret in the crypto object.          """          self._secret_id = secret_id -        self._crypto.secret = self._get_storage_secret()      def _load_secrets(self):          """ @@ -407,7 +406,7 @@ class Soledad(object):              content = json.loads(f.read())          self._secrets = content[self.STORAGE_SECRETS_KEY]          # choose first secret if no secret_id was given -        if self._secret_id == None: +        if self._secret_id is None:              self._set_secret_id(self._secrets.items()[0][0])          # check secret is isncrypted          if not self._crypto.is_encrypted_sym( @@ -952,7 +951,7 @@ class Soledad(object):          # set uuid          self._uuid = data[self.UUID_KEY]          # choose first secret to use -        self._set_secret_id(self._secrets.items()[0][0]) +        self._set_secret_id(data[self.STORAGE_SECRETS_KEY].items()[0][0])      #      # Setters/getters @@ -984,6 +983,10 @@ class Soledad(object):          _get_server_url,          doc='The URL of the Soledad server.') +    storage_secret = property( +        _get_storage_secret, +        doc='The secret used for symmetric encryption.') +  #-----------------------------------------------------------------------------  # Monkey patching u1db to be able to provide a custom SSL cert @@ -1004,8 +1007,7 @@ class VerifiedHTTPSConnection(httplib.HTTPSConnection):          self.sock = ssl.wrap_socket(sock,                                      ca_certs=SOLEDAD_CERT,                                      cert_reqs=ssl.CERT_REQUIRED) -        # TODO: enable this when the certificate is fixed -        #match_hostname(self.sock.getpeercert(), self.host) +        match_hostname(self.sock.getpeercert(), self.host)  old__VerifiedHTTPSConnection = http_client._VerifiedHTTPSConnection diff --git a/src/leap/soledad/auth.py b/src/leap/soledad/auth.py index 1d8f1a42..562a8263 100644 --- a/src/leap/soledad/auth.py +++ b/src/leap/soledad/auth.py @@ -44,7 +44,6 @@ class TokenBasedAuth(object):          """          self._creds = {'token': (uuid, token)} -      def _sign_request(self, method, url_query, params):          """          Return an authorization header to be included in the HTTP request, in @@ -67,4 +66,5 @@ class TokenBasedAuth(object):              auth = '%s:%s' % (uuid, token)              return [('Authorization', 'Token %s' % auth.encode('base64')[:-1])]          else: -            return HTTPClientBase._sign_request(self, method, url_query, params) +            return HTTPClientBase._sign_request( +                self, method, url_query, params) diff --git a/src/leap/soledad/backends/leap_backend.py b/src/leap/soledad/backends/leap_backend.py index 2585379a..8fa662e9 100644 --- a/src/leap/soledad/backends/leap_backend.py +++ b/src/leap/soledad/backends/leap_backend.py @@ -25,6 +25,9 @@ try:      import simplejson as json  except ImportError:      import json  # noqa +import hashlib +import hmac +import binascii  from u1db import Document @@ -33,10 +36,16 @@ from u1db.errors import BrokenSyncStream  from u1db.remote.http_target import HTTPSyncTarget +from leap.common.crypto import ( +    EncryptionMethods, +    encrypt_sym, +    decrypt_sym, +)  from leap.common.keymanager import KeyManager  from leap.common.check import leap_assert  from leap.soledad.auth import TokenBasedAuth +  #  # Exceptions  # @@ -48,10 +57,25 @@ class DocumentNotEncrypted(Exception):      pass -class UnknownEncryptionSchemes(Exception): +class UnknownEncryptionScheme(Exception):      """      Raised when trying to decrypt from unknown encryption schemes.      """ +    pass + + +class UnknownMacMethod(Exception): +    """ +    Raised when trying to authenticate document's content with unknown MAC +    mehtod. +    """ +    pass + + +class WrongMac(Exception): +    """ +    Raised when failing to authenticate document's contents based on MAC. +    """  # @@ -68,96 +92,164 @@ class EncryptionSchemes(object):      PUBKEY = 'pubkey' +class MacMethods(object): +    """ +    Representation of MAC methods used to authenticate document's contents. +    """ + +    HMAC = 'hmac' + +  #  # Crypto utilities for a LeapDocument.  #  ENC_JSON_KEY = '_enc_json'  ENC_SCHEME_KEY = '_enc_scheme' +ENC_METHOD_KEY = '_enc_method' +ENC_IV_KEY = '_enc_iv'  MAC_KEY = '_mac' +MAC_METHOD_KEY = '_mac_method' -def encrypt_doc_json(crypto, doc_id, doc_json): +def mac_doc(crypto, doc_id, doc_rev, ciphertext, mac_method):      """ -    Return a valid JSON string containing the C{doc} content encrypted to -    a symmetric key and the encryption scheme. +    Calculate a MAC for C{doc} using C{ciphertext}. -    The returned JSON string is the serialization of the following dictionary: +    Current MAC method used is HMAC, with the following parameters: + +        * key: sha256(storage_secret, doc_id) +        * msg: doc_id + doc_rev + ciphertext +        * digestmod: sha256 + +    @param crypto: A SoledadCryto instance used to perform the encryption. +    @type crypto: leap.soledad.crypto.SoledadCrypto +    @param doc_id: The id of the document. +    @type doc_id: str +    @param doc_rev: The revision of the document. +    @type doc_rev: str +    @param ciphertext: The content of the document. +    @type ciphertext: str +    @param mac_method: The MAC method to use. +    @type mac_method: str + +    @return: The calculated MAC. +    @rtype: str +    """ +    if mac_method == MacMethods.HMAC: +        return hmac.new( +            crypto.doc_mac_key(doc_id), +            str(doc_id) + str(doc_rev) + ciphertext, +            hashlib.sha256).digest() +    # raise if we do not know how to handle this MAC method +    raise UnknownMacMethod('Unknown MAC method: %s.' % mac_method) + + +def encrypt_doc(crypto, doc): +    """ +    Encrypt C{doc}'s content. + +    Encrypt doc's contents using AES-256 CTR mode and return a valid JSON +    string representing the following:          { -            '_enc_json': encrypt_sym(doc_content), -            '_enc_scheme': 'symkey', -            '_mac': <mac> [Not implemented yet] +            ENC_JSON_KEY: '<encrypted doc JSON string>', +            ENC_SCHEME_KEY: 'symkey', +            ENC_METHOD_KEY: EncryptionMethods.AES_256_CTR, +            ENC_IV_KEY: '<the initial value used to encrypt>', +            MAC_KEY: '<mac>' +            MAC_METHOD_KEY: 'hmac'          } -    @param crypto: A SoledadCryto instance to perform the encryption. +    @param crypto: A SoledadCryto instance used to perform the encryption.      @type crypto: leap.soledad.crypto.SoledadCrypto -    @param doc_id: The unique id of the document. -    @type doc_id: str -    @param doc_json: The JSON serialization of the document's contents. -    @type doc_json: str +    @param doc: The document with contents to be encrypted. +    @type doc: LeapDocument -    @return: The JSON serialization representing the encrypted content. +    @return: The JSON serialization of the dict representing the encrypted +        content.      @rtype: str      """ -    ciphertext = crypto.encrypt_sym( -        doc_json, -        crypto.passphrase_hash(doc_id)) -    if not crypto.is_encrypted_sym(ciphertext): -        raise DocumentNotEncrypted('Failed encrypting document.') +    leap_assert(doc.is_tombstone() is False) +    # encrypt content using AES-256 CTR mode +    iv, ciphertext = encrypt_sym( +        doc.get_json(), +        crypto.doc_passphrase(doc.doc_id), +        method=EncryptionMethods.AES_256_CTR) +    # Return a representation for the encrypted content. In the following, we +    # convert binary data to hexadecimal representation so the JSON +    # serialization does not complain about what it tries to serialize. +    hex_ciphertext = binascii.b2a_hex(ciphertext)      return json.dumps({ -        ENC_JSON_KEY: ciphertext, +        ENC_JSON_KEY: hex_ciphertext,          ENC_SCHEME_KEY: EncryptionSchemes.SYMKEY, +        ENC_METHOD_KEY: EncryptionMethods.AES_256_CTR, +        ENC_IV_KEY: iv, +        MAC_KEY: binascii.b2a_hex(mac_doc(  # store the mac as hex. +            crypto, doc.doc_id, doc.rev, +            ciphertext, +            MacMethods.HMAC)), +        MAC_METHOD_KEY: MacMethods.HMAC,      }) -def decrypt_doc_json(crypto, doc_id, doc_json): +def decrypt_doc(crypto, doc):      """ -    Return a JSON serialization of the decrypted content contained in -    C{encrypted_json}. +    Decrypt C{doc}'s content. + +    Return the JSON string representation of the document's decrypted content. -    The C{encrypted_json} parameter is the JSON serialization of the -    following dictionary: +    The content of the document should have the following structure:          { -            ENC_JSON_KEY: enc_blob, -            ENC_SCHEME_KEY: enc_scheme, +            ENC_JSON_KEY: '<enc_blob>', +            ENC_SCHEME_KEY: '<enc_scheme>', +            ENC_METHOD_KEY: '<enc_method>', +            ENC_IV_KEY: '<initial value used to encrypt>',  # (optional) +            MAC_KEY: '<mac>' +            MAC_METHOD_KEY: 'hmac'          }      C{enc_blob} is the encryption of the JSON serialization of the document's      content. For now Soledad just deals with documents whose C{enc_scheme} is -    EncryptionSchemes.SYMKEY. +    EncryptionSchemes.SYMKEY and C{enc_method} is +    EncryptionMethods.AES_256_CTR.      @param crypto: A SoledadCryto instance to perform the encryption.      @type crypto: leap.soledad.crypto.SoledadCrypto -    @param doc_id: The unique id of the document. -    @type doc_id: str -    @param doc_json: The JSON serialization representation of the encrypted -        document's contents. -    @type doc_json: str +    @param doc: The document to be decrypted. +    @type doc: LeapDocument      @return: The JSON serialization of the decrypted content.      @rtype: str      """ -    leap_assert(isinstance(doc_id, str), 'Document id is not a string.') -    leap_assert(doc_id != '', 'Received empty document id.') -    leap_assert(isinstance(doc_json, str), 'Document JSON is not a string.') -    leap_assert(doc_json != '', 'Received empty document JSON.') -    content = json.loads(doc_json) -    ciphertext = content[ENC_JSON_KEY] -    enc_scheme = content[ENC_SCHEME_KEY] +    leap_assert(doc.is_tombstone() is False) +    leap_assert(ENC_JSON_KEY in doc.content) +    leap_assert(ENC_SCHEME_KEY in doc.content) +    leap_assert(ENC_METHOD_KEY in doc.content) +    leap_assert(MAC_KEY in doc.content) +    leap_assert(MAC_METHOD_KEY in doc.content) +    # verify MAC +    ciphertext = binascii.a2b_hex(  # content is stored as hex. +        doc.content[ENC_JSON_KEY]) +    mac = mac_doc( +        crypto, doc.doc_id, doc.rev, +        ciphertext, +        doc.content[MAC_METHOD_KEY]) +    if binascii.a2b_hex(doc.content[MAC_KEY]) != mac:  # mac is stored as hex. +        raise WrongMac('Could not authenticate document\'s contents.') +    # decrypt doc's content +    enc_scheme = doc.content[ENC_SCHEME_KEY]      plainjson = None      if enc_scheme == EncryptionSchemes.SYMKEY: -        if not crypto.is_encrypted_sym(ciphertext): -            raise DocumentNotEncrypted( -                'Unable to identify document encryption for incoming ' -                'document, although it is marked as being encrypted with a ' -                'symmetric key.') -        plainjson = crypto.decrypt_sym( +        leap_assert(ENC_IV_KEY in doc.content) +        plainjson = decrypt_sym(              ciphertext, -            crypto.passphrase_hash(doc_id)) +            crypto.doc_passphrase(doc.doc_id), +            method=doc.content[ENC_METHOD_KEY], +            iv=doc.content[ENC_IV_KEY])      else: -        raise UnknownEncryptionSchemes(enc_scheme) +        raise UnknownEncryptionScheme(enc_scheme)      return plainjson @@ -354,9 +446,7 @@ class LeapSyncTarget(HTTPSyncTarget, TokenBasedAuth):                  if doc.content and ENC_SCHEME_KEY in doc.content:                      if doc.content[ENC_SCHEME_KEY] == \                              EncryptionSchemes.SYMKEY: -                        doc.set_json( -                            decrypt_doc_json( -                                self._crypto, doc.doc_id, entry['content'])) +                        doc.set_json(decrypt_doc(self._crypto, doc))                  #-------------------------------------------------------------                  # end of symmetric decryption                  #------------------------------------------------------------- @@ -433,15 +523,14 @@ class LeapSyncTarget(HTTPSyncTarget, TokenBasedAuth):              #-------------------------------------------------------------              # symmetric encryption of document's contents              #------------------------------------------------------------- -            enc_json = doc.get_json() +            doc_json = doc.get_json()              if not doc.is_tombstone(): -                enc_json = encrypt_doc_json( -                    self._crypto, doc.doc_id, doc.get_json()) +                doc_json = encrypt_doc(self._crypto, doc)              #-------------------------------------------------------------              # end of symmetric encryption              #-------------------------------------------------------------              size += prepare(id=doc.doc_id, rev=doc.rev, -                            content=enc_json, +                            content=doc_json,                              gen=gen, trans_id=trans_id)          entries.append('\r\n]')          size += len(entries[-1]) diff --git a/src/leap/soledad/crypto.py b/src/leap/soledad/crypto.py index 605380ec..d0e2c720 100644 --- a/src/leap/soledad/crypto.py +++ b/src/leap/soledad/crypto.py @@ -21,7 +21,8 @@ Cryptographic utilities for Soledad.  """ -from hashlib import sha256 +import hmac +import hashlib  from leap.common.keymanager import openpgp @@ -38,6 +39,8 @@ class SoledadCrypto(object):      General cryptographic functionality.      """ +    MAC_KEY_LENGTH = 64 +      def __init__(self, soledad):          """          Initialize the crypto object. @@ -47,7 +50,6 @@ class SoledadCrypto(object):          """          self._soledad = soledad          self._pgp = openpgp.OpenPGPScheme(self._soledad) -        self._secret = None      def encrypt_sym(self, data, passphrase):          """ @@ -98,33 +100,61 @@ class SoledadCrypto(object):          """          return openpgp.is_encrypted_sym(data) -    def passphrase_hash(self, suffix): +    def doc_passphrase(self, doc_id):          """ -        Generate a passphrase for symmetric encryption. +        Generate a passphrase for symmetric encryption of document's contents. -        The password is derived from the secret for symmetric encryption and -        a C{suffix} that is appended to the secret prior to hashing. +        The password is derived using HMAC having sha256 as underlying hash +        function. The key used for HMAC is Soledad's storage secret stripped +        from the first MAC_KEY_LENGTH characters. The HMAC message is +        C{doc_id}. -        @param suffix: Will be appended to the symmetric key before hashing. -        @type suffix: str +        @param doc_id: The id of the document that will be encrypted using +            this passphrase. +        @type doc_id: str -        @return: the passphrase +        @return: The passphrase.          @rtype: str +          @raise NoSymmetricSecret: if no symmetric secret was supplied.          """ -        if self._secret is None: +        if self.secret is None:              raise NoSymmetricSecret() -        return sha256('%s%s' % (self._secret, suffix)).hexdigest() +        return hmac.new( +            self.secret[self.MAC_KEY_LENGTH:], +            doc_id, +            hashlib.sha256).digest() + +    def doc_mac_key(self, doc_id): +        """ +        Generate a key for calculating a MAC for a document whose id is +        C{doc_id}. + +        The key is derived using HMAC having sha256 as underlying hash +        function. The key used for HMAC is the first MAC_KEY_LENGTH characters +        of Soledad's storage secret. The HMAC message is C{doc_id}. + +        @param doc_id: The id of the document. +        @type doc_id: str + +        @return: The key. +        @rtype: str + +        @raise NoSymmetricSecret: if no symmetric secret was supplied. +        """ +        if self.secret is None: +            raise NoSymmetricSecret() +        return hmac.new( +            self.secret[:self.MAC_KEY_LENGTH], +            doc_id, +            hashlib.sha256).digest()      #      # secret setters/getters      #      def _get_secret(self): -        return self._secret - -    def _set_secret(self, secret): -        self._secret = secret +        return self._soledad.storage_secret -    secret = property(_get_secret, _set_secret, -                      doc='The key used for symmetric encryption') +    secret = property( +        _get_secret, doc='The secret used for symmetric encryption') diff --git a/src/leap/soledad/server.py b/src/leap/soledad/server.py index e7b55a3e..7aa253a3 100644 --- a/src/leap/soledad/server.py +++ b/src/leap/soledad/server.py @@ -187,7 +187,6 @@ class SoledadAuthMiddleware(object):          return not environ.get(self.PATH_INFO_KEY).startswith('/shared/') -  #-----------------------------------------------------------------------------  # Soledad WSGI application  #----------------------------------------------------------------------------- diff --git a/src/leap/soledad/tests/__init__.py b/src/leap/soledad/tests/__init__.py index 6787aa9d..79ee69c4 100644 --- a/src/leap/soledad/tests/__init__.py +++ b/src/leap/soledad/tests/__init__.py @@ -10,7 +10,7 @@ from leap.soledad import Soledad  from leap.soledad.crypto import SoledadCrypto  from leap.soledad.backends.leap_backend import (      LeapDocument, -    decrypt_doc_json, +    decrypt_doc,      ENC_SCHEME_KEY,  )  from leap.common.testing.basetest import BaseLeapTest @@ -23,7 +23,7 @@ from leap.common.testing.basetest import BaseLeapTest  class BaseSoledadTest(BaseLeapTest):      """ -    Instantiates GPG and Soledad for usage in tests. +    Instantiates Soledad for usage in tests.      """      def setUp(self): @@ -44,7 +44,8 @@ class BaseSoledadTest(BaseLeapTest):          self._db2.close()          self._soledad.close() -    def _soledad_instance(self, user='leap@leap.se', passphrase='123', prefix='', +    def _soledad_instance(self, user='leap@leap.se', passphrase='123', +                          prefix='',                            secrets_path=Soledad.STORAGE_SECRETS_FILE_NAME,                            local_db_path='/soledad.u1db', server_url='',                            cert_file=None): @@ -69,15 +70,16 @@ class BaseSoledadTest(BaseLeapTest):              server_url=server_url,  # Soledad will fail if not given an url.              cert_file=cert_file) -    def assertGetEncryptedDoc(self, db, doc_id, doc_rev, content, has_conflicts): -        """Assert that the document in the database looks correct.""" +    def assertGetEncryptedDoc( +            self, db, doc_id, doc_rev, content, has_conflicts): +        """ +        Assert that the document in the database looks correct. +        """          exp_doc = self.make_document(doc_id, doc_rev, content,                                       has_conflicts=has_conflicts)          doc = db.get_doc(doc_id)          if ENC_SCHEME_KEY in doc.content: -            doc.set_json( -                decrypt_doc_json( -                    self._soledad._crypto, doc.doc_id, doc.get_json())) +            doc.set_json(decrypt_doc(self._soledad._crypto, doc))          self.assertEqual(exp_doc.doc_id, doc.doc_id)          self.assertEqual(exp_doc.rev, doc.rev)          self.assertEqual(exp_doc.has_conflicts, doc.has_conflicts) diff --git a/src/leap/soledad/tests/test_crypto.py b/src/leap/soledad/tests/test_crypto.py index 61c5f5b0..9a219bd0 100644 --- a/src/leap/soledad/tests/test_crypto.py +++ b/src/leap/soledad/tests/test_crypto.py @@ -31,13 +31,16 @@ except ImportError:  from leap.soledad.backends.leap_backend import (      LeapDocument, -    encrypt_doc_json, -    decrypt_doc_json, +    encrypt_doc, +    decrypt_doc,      EncryptionSchemes,      LeapSyncTarget,      ENC_JSON_KEY,      ENC_SCHEME_KEY, +    MAC_METHOD_KEY,      MAC_KEY, +    UnknownMacMethod, +    WrongMac,  )  from leap.soledad.backends.couch import CouchDatabase  from leap.soledad import KeyAlreadyExists, Soledad @@ -66,16 +69,21 @@ class EncryptedSyncTestCase(BaseSoledadTest):          """          Test encrypting and decrypting documents.          """ +        simpledoc = {'key': 'val'}          doc1 = LeapDocument(doc_id='id') -        doc1.content = {'key': 'val'} -        enc_json = encrypt_doc_json( -            self._soledad._crypto, doc1.doc_id, doc1.get_json()) -        plain_json = decrypt_doc_json( -            self._soledad._crypto, doc1.doc_id, enc_json) -        doc2 = LeapDocument(doc_id=doc1.doc_id, json=plain_json) -        res1 = doc1.get_json() -        res2 = doc2.get_json() -        self.assertEqual(res1, res2, 'incorrect document encryption') +        doc1.content = simpledoc +        # encrypt doc +        doc1.set_json(encrypt_doc(self._soledad._crypto, doc1)) +        # assert content is different and includes keys +        self.assertNotEqual( +            simpledoc, doc1.content, +            'incorrect document encryption') +        self.assertTrue(ENC_JSON_KEY in doc1.content) +        self.assertTrue(ENC_SCHEME_KEY in doc1.content) +        # decrypt doc +        doc1.set_json(decrypt_doc(self._soledad._crypto, doc1)) +        self.assertEqual( +            simpledoc, doc1.content, 'incorrect document encryption')      def test_encrypt_sym(self):          """ @@ -84,9 +92,7 @@ class EncryptedSyncTestCase(BaseSoledadTest):          doc1 = LeapDocument()          doc1.content = {'key': 'val'}          enc_json = json.loads( -            encrypt_doc_json( -                self._soledad._crypto, -                doc1.doc_id, doc1.get_json()))[ENC_JSON_KEY] +            encrypt_doc(self._soledad._crypto, doc1))[ENC_JSON_KEY]          self.assertEqual(              True,              self._soledad._crypto.is_encrypted_sym(enc_json), @@ -161,7 +167,7 @@ class EncryptedSyncTestCase(BaseSoledadTest):  #        # create and encrypt a doc to insert directly in couchdb  #        doc = LeapDocument('doc-id')  #        doc.set_json( -#            encrypt_doc_json( +#            encrypt_doc(  #                self._soledad._crypto, 'doc-id', json.dumps(simple_doc)))  #        db.put_doc(doc)  #        # setup credentials for access to soledad server @@ -241,3 +247,42 @@ class CryptoMethodsTestCase(BaseSoledadTest):          sol = self._soledad_instance(user='user@leap.se', prefix='/3')          self.assertTrue(sol._has_secret(), "Should have a secret at "                                             "this point") + + +class MacAuthTestCase(BaseSoledadTest): + +    def test_decrypt_with_wrong_mac_raises(self): +        """ +        Trying to decrypt a document with wrong MAC should raise. +        """ +        simpledoc = {'key': 'val'} +        doc = LeapDocument(doc_id='id') +        doc.content = simpledoc +        # encrypt doc +        doc.set_json(encrypt_doc(self._soledad._crypto, doc)) +        self.assertTrue(MAC_KEY in doc.content) +        self.assertTrue(MAC_METHOD_KEY in doc.content) +        # mess with MAC +        doc.content[MAC_KEY] = 'wrongmac' +        # try to decrypt doc +        self.assertRaises( +            WrongMac, +            decrypt_doc, self._soledad._crypto, doc) + +    def test_decrypt_with_unknown_mac_method_raises(self): +        """ +        Trying to decrypt a document with unknown MAC method should raise. +        """ +        simpledoc = {'key': 'val'} +        doc = LeapDocument(doc_id='id') +        doc.content = simpledoc +        # encrypt doc +        doc.set_json(encrypt_doc(self._soledad._crypto, doc)) +        self.assertTrue(MAC_KEY in doc.content) +        self.assertTrue(MAC_METHOD_KEY in doc.content) +        # mess with MAC method +        doc.content[MAC_METHOD_KEY] = 'mymac' +        # try to decrypt doc +        self.assertRaises( +            UnknownMacMethod, +            decrypt_doc, self._soledad._crypto, doc) diff --git a/src/leap/soledad/tests/test_leap_backend.py b/src/leap/soledad/tests/test_leap_backend.py index dbebadb5..8afae6f6 100644 --- a/src/leap/soledad/tests/test_leap_backend.py +++ b/src/leap/soledad/tests/test_leap_backend.py @@ -106,7 +106,8 @@ def make_token_http_database_for_test(test, replica_uid):              auth.TokenBasedAuth.set_token_credentials(self, uuid, token)          def _sign_request(self, method, url_query, params): -            return auth.TokenBasedAuth._sign_request(self, method, url_query, params) +            return auth.TokenBasedAuth._sign_request( +                self, method, url_query, params)      http_db = _HTTPDatabaseWithToken(test.getURL('test'))      http_db.set_token_credentials('user-uuid', 'auth-token') @@ -162,7 +163,8 @@ class TestLeapClientBase(test_http_client.TestHTTPClientBase):                  auth.TokenBasedAuth.set_token_credentials(self, uuid, token)              def _sign_request(self, method, url_query, params): -                return auth.TokenBasedAuth._sign_request(self, method, url_query, params) +                return auth.TokenBasedAuth._sign_request( +                    self, method, url_query, params)          return _HTTPClientWithToken(self.getURL('dbase'), **kwds) @@ -185,7 +187,8 @@ class TestLeapClientBase(test_http_client.TestHTTPClientBase):          pass      def app(self, environ, start_response): -        res = test_http_client.TestHTTPClientBase.app(self, environ, start_response) +        res = test_http_client.TestHTTPClientBase.app( +            self, environ, start_response)          if res is not None:              return res          # mime solead application here. @@ -195,13 +198,13 @@ class TestLeapClientBase(test_http_client.TestHTTPClientBase):                  start_response("401 Unauthorized",                                 [('Content-Type', 'application/json')])                  return [json.dumps({"error": "unauthorized", -                                        "message": e.message})] +                                    "message": e.message})]              scheme, encoded = auth.split(None, 1)              if scheme.lower() != 'token':                  start_response("401 Unauthorized",                                 [('Content-Type', 'application/json')])                  return [json.dumps({"error": "unauthorized", -                                        "message": e.message})] +                                    "message": e.message})]              uuid, token = encoded.decode('base64').split(':', 1)              if uuid != 'user-uuid' and token != 'auth-token':                  return unauth_err("Incorrect address or token.") @@ -228,7 +231,6 @@ class TestLeapClientBase(test_http_client.TestHTTPClientBase):              ['/dbase/doc/token', 'user-uuid', 'auth-token'], json.loads(res)) -  #-----------------------------------------------------------------------------  # The following tests come from `u1db.tests.test_document`.  #----------------------------------------------------------------------------- @@ -284,10 +286,9 @@ class TestLeapParsingSyncStream(          """          Test adapted to use encrypted content.          """ -        doc = leap_backend.LeapDocument('i') +        doc = leap_backend.LeapDocument('i', rev='r')          doc.content = {} -        enc_json = leap_backend.encrypt_doc_json( -            self._soledad._crypto, doc.doc_id, doc.get_json()) +        enc_json = leap_backend.encrypt_doc(self._soledad._crypto, doc)          tgt = leap_backend.LeapSyncTarget(              "http://foo/foo", crypto=self._soledad._crypto) @@ -367,6 +368,7 @@ def oauth_leap_sync_target(test, path):                               tests.token1.key, tests.token1.secret)      return st +  def token_leap_sync_target(test, path):      st = leap_sync_target(test, path)      st.set_token_credentials('user-uuid', 'auth-token') @@ -374,7 +376,7 @@ def token_leap_sync_target(test, path):  class TestLeapSyncTarget( -    test_remote_sync_target.TestRemoteSyncTargets, BaseSoledadTest): +        test_remote_sync_target.TestRemoteSyncTargets, BaseSoledadTest):      scenarios = [          ('http', {'make_app_with_state': make_soledad_app, @@ -383,9 +385,10 @@ class TestLeapSyncTarget(          ('oauth_http', {'make_app_with_state': make_oauth_http_app,                          'make_document_for_test': make_leap_document_for_test,                          'sync_target': oauth_leap_sync_target}), -        ('token_soledad', {'make_app_with_state': make_token_soledad_app, -                        'make_document_for_test': make_leap_document_for_test, -                        'sync_target': token_leap_sync_target}), +        ('token_soledad', +            {'make_app_with_state': make_token_soledad_app, +             'make_document_for_test': make_leap_document_for_test, +             'sync_target': token_leap_sync_target}),      ]      def test_sync_exchange_send(self): @@ -523,10 +526,11 @@ class TestLeapSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSupport,                                       BaseSoledadTest):      scenarios = [ -        ('token_soledad_https', {'server_def': test_https.https_server_def, -                        'make_app_with_state': make_token_soledad_app, -                        'make_document_for_test': make_leap_document_for_test, -                        'sync_target': token_leap_https_sync_target}), +        ('token_soledad_https', +            {'server_def': test_https.https_server_def, +             'make_app_with_state': make_token_soledad_app, +             'make_document_for_test': make_leap_document_for_test, +             'sync_target': token_leap_https_sync_target}),      ]      def setUp(self): @@ -568,6 +572,7 @@ class TestLeapSyncTargetHttpsSupport(test_https.TestHttpSyncTargetHttpsSupport,              http_client.CertificateError, remote_target.record_sync_info,              'other-id', 2, 'T-id') +  #-----------------------------------------------------------------------------  # The following tests come from `u1db.tests.test_http_database`.  #----------------------------------------------------------------------------- @@ -585,7 +590,8 @@ class _HTTPDatabase(http_database.HTTPDatabase, auth.TokenBasedAuth):              self, method, url_query, params) -class TestHTTPDatabaseWithCreds(test_http_database.TestHTTPDatabaseCtrWithCreds): +class TestHTTPDatabaseWithCreds( +        test_http_database.TestHTTPDatabaseCtrWithCreds):      def test_get_sync_target_inherits_token_credentials(self):          # this test was from TestDatabaseSimpleOperations but we put it here @@ -595,7 +601,6 @@ class TestHTTPDatabaseWithCreds(test_http_database.TestHTTPDatabaseCtrWithCreds)          st = self.db.get_sync_target()          self.assertEqual(self.db._creds, st._creds) -      def test_ctr_with_creds(self):          db1 = _HTTPDatabase('http://dbs/db', creds={'token': {              'uuid': 'user-uuid', @@ -658,7 +663,6 @@ class LeapDatabaseSyncTargetTests(                           (self.other_changes, new_gen, last_trans_id))          self.assertEqual(10, self.st.get_sync_info('replica')[3]) -      def test_sync_exchange_push_many(self):          """          Test sync exchange. @@ -666,9 +670,10 @@ class LeapDatabaseSyncTargetTests(          This test was adapted to decrypt remote content before assert.          """          docs_by_gen = [ -            (self.make_document('doc-id', 'replica:1', tests.simple_doc), 10, 'T-1'), -            (self.make_document('doc-id2', 'replica:1', tests.nested_doc), 11, -             'T-2')] +            (self.make_document( +                'doc-id', 'replica:1', tests.simple_doc), 10, 'T-1'), +            (self.make_document( +                'doc-id2', 'replica:1', tests.nested_doc), 11, 'T-2')]          new_gen, trans_id = self.st.sync_exchange(              docs_by_gen, 'replica', last_known_generation=0,              last_known_trans_id=None, return_doc_cb=self.receive_doc) @@ -682,7 +687,6 @@ class LeapDatabaseSyncTargetTests(                           (self.other_changes, new_gen, trans_id))          self.assertEqual(11, self.st.get_sync_info('replica')[3]) -      def test_sync_exchange_returns_many_new_docs(self):          """          Test sync exchange. @@ -766,10 +770,10 @@ class TestLeapDbSync(test_sync.TestDbSync, BaseSoledadTest):          self.assertEqual(1, len(changes))          self.assertEqual(doc2.doc_id, changes[0][0])          self.assertEqual(1, gen - local_gen_before_sync) -        self.assertGetEncryptedDoc(self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, -                          False) -        self.assertGetEncryptedDoc(self.db, doc2.doc_id, doc2.rev, tests.nested_doc, -                          False) +        self.assertGetEncryptedDoc( +            self.db2, doc1.doc_id, doc1.rev, tests.simple_doc, False) +        self.assertGetEncryptedDoc( +            self.db, doc2.doc_id, doc2.rev, tests.nested_doc, False)      def test_db_sync_autocreate(self):          """ @@ -785,8 +789,8 @@ class TestLeapDbSync(test_sync.TestDbSync, BaseSoledadTest):          gen, _, changes = db3.whats_changed()          self.assertEqual(1, len(changes))          self.assertEqual(doc1.doc_id, changes[0][0]) -        self.assertGetEncryptedDoc(db3, doc1.doc_id, doc1.rev, tests.simple_doc, -                          False) +        self.assertGetEncryptedDoc( +            db3, doc1.doc_id, doc1.rev, tests.simple_doc, False)          t_gen, _ = self.db._get_replica_gen_and_trans_id('test3.db')          s_gen, _ = db3._get_replica_gen_and_trans_id('test1')          self.assertEqual(1, t_gen) diff --git a/src/leap/soledad/tests/test_soledad.py b/src/leap/soledad/tests/test_soledad.py index 49358ab6..6a4261c0 100644 --- a/src/leap/soledad/tests/test_soledad.py +++ b/src/leap/soledad/tests/test_soledad.py @@ -69,7 +69,8 @@ class AuxMethodsTestCase(BaseSoledadTest):              secrets_path=None, local_db_path=None,              server_url='', cert_file=None)  # otherwise Soledad will fail.          self.assertEquals( -            os.path.join(sol.DEFAULT_PREFIX, Soledad.STORAGE_SECRETS_FILE_NAME), +            os.path.join( +                sol.DEFAULT_PREFIX, Soledad.STORAGE_SECRETS_FILE_NAME),              sol.secrets_path)          self.assertEquals(              os.path.join(sol.DEFAULT_PREFIX, 'soledad.u1db'), diff --git a/src/leap/soledad/tests/test_sqlcipher.py b/src/leap/soledad/tests/test_sqlcipher.py index c4282c0f..60261111 100644 --- a/src/leap/soledad/tests/test_sqlcipher.py +++ b/src/leap/soledad/tests/test_sqlcipher.py @@ -52,10 +52,9 @@ from leap.soledad.backends.sqlcipher import open as u1db_open  from leap.soledad.backends.leap_backend import (      LeapDocument,      EncryptionSchemes, -    decrypt_doc_json, +    decrypt_doc,      ENC_JSON_KEY,      ENC_SCHEME_KEY, -    MAC_KEY,  ) @@ -634,9 +633,7 @@ class SQLCipherDatabaseSyncTests(          self.sync(self.db2, db3)          doc3 = db3.get_doc('the-doc')          if ENC_SCHEME_KEY in doc3.content: -            doc3.set_json( -                decrypt_doc_json( -                    self._soledad._crypto, doc3.doc_id, doc3.get_json())) +            doc3.set_json(decrypt_doc(self._soledad._crypto, doc3))          self.assertEqual(doc4.get_json(), doc3.get_json())          self.assertFalse(doc3.has_conflicts) @@ -715,7 +712,8 @@ class SQLCipherSyncTargetTests(          sever-side.          """          docs_by_gen = [ -            (self.make_document('doc-id', 'replica:1', tests.simple_doc), 10, 'T-1'), +            (self.make_document( +                'doc-id', 'replica:1', tests.simple_doc), 10, 'T-1'),              (self.make_document('doc-id2', 'replica:1', tests.nested_doc), 11,               'T-2')]          new_gen, trans_id = self.st.sync_exchange( diff --git a/src/leap/soledad/tests/u1db_tests/test_https.py b/src/leap/soledad/tests/u1db_tests/test_https.py index b4b14722..62180f8c 100644 --- a/src/leap/soledad/tests/u1db_tests/test_https.py +++ b/src/leap/soledad/tests/u1db_tests/test_https.py @@ -74,7 +74,8 @@ class TestHttpSyncTargetHttpsSupport(tests.TestCaseWithServer):          # class with one that will do HTTPS independent of the platform. In          # order to maintain the compatibility with u1db default tests, we undo          # that replacement here. -        http_client._VerifiedHTTPSConnection = soledad.old__VerifiedHTTPSConnection +        http_client._VerifiedHTTPSConnection = \ +            soledad.old__VerifiedHTTPSConnection          super(TestHttpSyncTargetHttpsSupport, self).setUp()      def getSyncTarget(self, host, path=None): | 
