diff options
| -rw-r--r-- | client/changes/feature_6400_include-iv-in-document-mac | 1 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/crypto.py | 174 | ||||
| -rw-r--r-- | client/src/leap/soledad/client/secrets.py | 79 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/crypto.py | 8 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/tests/__init__.py | 2 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/tests/test_crypto.py | 52 | ||||
| -rw-r--r-- | common/src/leap/soledad/common/tests/test_soledad.py | 62 | 
7 files changed, 195 insertions, 183 deletions
| diff --git a/client/changes/feature_6400_include-iv-in-document-mac b/client/changes/feature_6400_include-iv-in-document-mac new file mode 100644 index 00000000..d8c9c9cc --- /dev/null +++ b/client/changes/feature_6400_include-iv-in-document-mac @@ -0,0 +1 @@ +  o Include the IV in the encrypted document MAC (#6400). diff --git a/client/src/leap/soledad/client/crypto.py b/client/src/leap/soledad/client/crypto.py index 681bf4f7..d6d9a618 100644 --- a/client/src/leap/soledad/client/crypto.py +++ b/client/src/leap/soledad/client/crypto.py @@ -32,18 +32,8 @@ from zope.proxy import sameProxiedObjects  from leap.soledad.common import soledad_assert  from leap.soledad.common import soledad_assert_type +from leap.soledad.common import crypto  from leap.soledad.common.document import SoledadDocument -from leap.soledad.common.crypto import EncryptionSchemes -from leap.soledad.common.crypto import EncryptionMethods -from leap.soledad.common.crypto import MacMethods -from leap.soledad.common.crypto import UnknownMacMethod -from leap.soledad.common.crypto import WrongMac -from leap.soledad.common.crypto import ENC_JSON_KEY -from leap.soledad.common.crypto import ENC_SCHEME_KEY -from leap.soledad.common.crypto import ENC_METHOD_KEY -from leap.soledad.common.crypto import ENC_IV_KEY -from leap.soledad.common.crypto import MAC_KEY -from leap.soledad.common.crypto import MAC_METHOD_KEY  logger = logging.getLogger(__name__) @@ -59,13 +49,16 @@ def _assert_known_encryption_method(method):      :param method: The encryption method to assert.      :type method: str -    :raise AssertionError: Raised if C{method} is unknown. +    :raise UnknownEncryptionMethodError: Raised when C{method} is unknown.      """      valid_methods = [ -        EncryptionMethods.AES_256_CTR, -        EncryptionMethods.XSALSA20, +        crypto.EncryptionMethods.AES_256_CTR, +        crypto.EncryptionMethods.XSALSA20,      ] -    soledad_assert(method in valid_methods) +    try: +        soledad_assert(method in valid_methods) +    except AssertionError: +        raise crypto.UnknownEncryptionMethodError  def encrypt_sym(data, key, method): @@ -96,11 +89,11 @@ def encrypt_sym(data, key, method):      iv = None      # AES-256 in CTR mode -    if method == EncryptionMethods.AES_256_CTR: +    if method == crypto.EncryptionMethods.AES_256_CTR:          iv = os.urandom(16)          ciphertext = AES(key=key, iv=iv).process(data)      # XSalsa20 -    elif method == EncryptionMethods.XSALSA20: +    elif method == crypto.EncryptionMethods.XSALSA20:          iv = os.urandom(24)          ciphertext = XSalsa20(key=key, iv=iv).process(data) @@ -125,7 +118,7 @@ def decrypt_sym(data, key, method, **kwargs):      :return: The decrypted data.      :rtype: str -    :raise AssertionError: Raised if C{method} is unknown. +    :raise UnknownEncryptionMethodError: Raised when C{method} is unknown.      """      soledad_assert_type(key, str)      # assert params @@ -137,10 +130,10 @@ def decrypt_sym(data, key, method, **kwargs):          '%s needs an initial value.' % method)      _assert_known_encryption_method(method)      # AES-256 in CTR mode -    if method == EncryptionMethods.AES_256_CTR: +    if method == crypto.EncryptionMethods.AES_256_CTR:          return AES(              key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) -    elif method == EncryptionMethods.XSALSA20: +    elif method == crypto.EncryptionMethods.XSALSA20:          return XSalsa20(              key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) @@ -185,11 +178,11 @@ class SoledadCrypto(object):          self._soledad = soledad      def encrypt_sym(self, data, key, -                    method=EncryptionMethods.AES_256_CTR): +                    method=crypto.EncryptionMethods.AES_256_CTR):          return encrypt_sym(data, key, method)      def decrypt_sym(self, data, key, -                    method=EncryptionMethods.AES_256_CTR, **kwargs): +                    method=crypto.EncryptionMethods.AES_256_CTR, **kwargs):          return decrypt_sym(data, key, method, **kwargs)      def doc_mac_key(self, doc_id, secret): @@ -233,7 +226,8 @@ class SoledadCrypto(object):  # Crypto utilities for a SoledadDocument.  # -def mac_doc(doc_id, doc_rev, ciphertext, mac_method, secret): +def mac_doc(doc_id, doc_rev, ciphertext, enc_scheme, enc_method, enc_iv, +        mac_method, secret):      """      Calculate a MAC for C{doc} using C{ciphertext}. @@ -249,6 +243,12 @@ def mac_doc(doc_id, doc_rev, ciphertext, mac_method, secret):      :type doc_rev: str      :param ciphertext: The content of the document.      :type ciphertext: str +    :param enc_scheme: The encryption scheme. +    :type enc_scheme: str +    :param enc_method: The encryption method. +    :type enc_method: str +    :param enc_iv: The encryption initialization vector. +    :type enc_iv: str      :param mac_method: The MAC method to use.      :type mac_method: str      :param secret: The Soledad storage secret @@ -257,15 +257,20 @@ def mac_doc(doc_id, doc_rev, ciphertext, mac_method, secret):      :return: The calculated MAC.      :rtype: str -    :raise UnknownMacMethod: Raised when C{mac_method} is unknown. +    :raise crypto.UnknownMacMethodError: Raised when C{mac_method} is unknown.      """      try: -        soledad_assert(mac_method == MacMethods.HMAC) +        soledad_assert(mac_method == crypto.MacMethods.HMAC)      except AssertionError: -        raise UnknownMacMethod -    content = str(doc_id) \ -        + str(doc_rev) \ -        + ciphertext +        raise crypto.UnknownMacMethodError +    template = "{doc_id}{doc_rev}{ciphertext}{enc_scheme}{enc_method}{enc_iv}" +    content = template.format( +        doc_id=doc_id, +        doc_rev=doc_rev, +        ciphertext=ciphertext, +        enc_scheme=enc_scheme, +        enc_method=enc_method, +        enc_iv=enc_iv)      return hmac.new(          doc_mac_key(doc_id, secret),          content, @@ -297,12 +302,12 @@ def encrypt_docstr(docstr, doc_id, doc_rev, key, secret):      string representing the following:          { -            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>', +            crypto.ENC_JSON_KEY: '<encrypted doc JSON string>', +            crypto.ENC_SCHEME_KEY: 'symkey', +            crypto.ENC_METHOD_KEY: crypto.EncryptionMethods.AES_256_CTR, +            crypto.ENC_IV_KEY: '<the initial value used to encrypt>',              MAC_KEY: '<mac>' -            MAC_METHOD_KEY: 'hmac' +            crypto.MAC_METHOD_KEY: 'hmac'          }      :param docstr: A representation of the document to be encrypted. @@ -324,10 +329,10 @@ def encrypt_docstr(docstr, doc_id, doc_rev, key, secret):               content.      :rtype: str      """ -    enc_scheme = EncryptionSchemes.SYMKEY -    enc_method = EncryptionMethods.AES_256_CTR -    mac_method = MacMethods.HMAC -    iv, ciphertext = encrypt_sym( +    enc_scheme = crypto.EncryptionSchemes.SYMKEY +    enc_method = crypto.EncryptionMethods.AES_256_CTR +    mac_method = crypto.MacMethods.HMAC +    enc_iv, ciphertext = encrypt_sym(          str(docstr),  # encryption/decryption routines expect str          key, method=enc_method)      mac = binascii.b2a_hex(  # store the mac as hex. @@ -335,6 +340,9 @@ def encrypt_docstr(docstr, doc_id, doc_rev, key, secret):              doc_id,              doc_rev,              ciphertext, +            enc_scheme, +            enc_method, +            enc_iv,              mac_method,              secret))      # Return a representation for the encrypted content. In the following, we @@ -342,12 +350,12 @@ def encrypt_docstr(docstr, doc_id, doc_rev, key, secret):      # serialization does not complain about what it tries to serialize.      hex_ciphertext = binascii.b2a_hex(ciphertext)      return json.dumps({ -        ENC_JSON_KEY: hex_ciphertext, -        ENC_SCHEME_KEY: enc_scheme, -        ENC_METHOD_KEY: enc_method, -        ENC_IV_KEY: iv, -        MAC_KEY: mac, -        MAC_METHOD_KEY: mac_method, +        crypto.ENC_JSON_KEY: hex_ciphertext, +        crypto.ENC_SCHEME_KEY: enc_scheme, +        crypto.ENC_METHOD_KEY: enc_method, +        crypto.ENC_IV_KEY: enc_iv, +        crypto.MAC_KEY: mac, +        crypto.MAC_METHOD_KEY: mac_method,      }) @@ -369,7 +377,8 @@ def decrypt_doc(crypto, doc):      return decrypt_doc_dict(doc.content, doc.doc_id, doc.rev, key, secret) -def _verify_doc_mac(doc_id, doc_rev, ciphertext, mac_method, secret, doc_mac): +def _verify_doc_mac(doc_id, doc_rev, ciphertext, enc_scheme, enc_method, +        enc_iv, mac_method, secret, doc_mac):      """      Verify that C{doc_mac} is a correct MAC for the given document. @@ -379,6 +388,12 @@ def _verify_doc_mac(doc_id, doc_rev, ciphertext, mac_method, secret, doc_mac):      :type doc_rev: str      :param ciphertext: The content of the document.      :type ciphertext: str +    :param enc_scheme: The encryption scheme. +    :type enc_scheme: str +    :param enc_method: The encryption method. +    :type enc_method: str +    :param enc_iv: The encryption initialization vector. +    :type enc_iv: str      :param mac_method: The MAC method to use.      :type mac_method: str      :param secret: The Soledad storage secret @@ -386,12 +401,16 @@ def _verify_doc_mac(doc_id, doc_rev, ciphertext, mac_method, secret, doc_mac):      :param doc_mac: The MAC to be verified against.      :type doc_mac: str -    :raise UnknownMacMethod: Raised when C{mac_method} is unknown. +    :raise crypto.UnknownMacMethodError: Raised when C{mac_method} is unknown. +    :raise crypto.WrongMacError: Raised when MAC could not be verified.      """      calculated_mac = mac_doc(          doc_id,          doc_rev,          ciphertext, +        enc_scheme, +        enc_method, +        enc_iv,          mac_method,          secret)      # we compare mac's hashes to avoid possible timing attacks that might @@ -404,7 +423,8 @@ def _verify_doc_mac(doc_id, doc_rev, ciphertext, mac_method, secret, doc_mac):      if doc_mac_hash != calculated_mac_hash:          logger.warning("Wrong MAC while decrypting doc...") -        raise WrongMac('Could not authenticate document\'s contents.') +        raise crypto.WrongMacError("Could not authenticate document's " +                                   "contents.")  def decrypt_doc_dict(doc_dict, doc_id, doc_rev, key, secret): @@ -416,18 +436,18 @@ def decrypt_doc_dict(doc_dict, doc_id, doc_rev, key, secret):      The passed doc_dict argument should have the following structure:          { -            ENC_JSON_KEY: '<enc_blob>', -            ENC_SCHEME_KEY: '<enc_scheme>', -            ENC_METHOD_KEY: '<enc_method>', -            ENC_IV_KEY: '<initial value used to encrypt>',  # (optional) +            crypto.ENC_JSON_KEY: '<enc_blob>', +            crypto.ENC_SCHEME_KEY: '<enc_scheme>', +            crypto.ENC_METHOD_KEY: '<enc_method>', +            crypto.ENC_IV_KEY: '<initial value used to encrypt>',  # (optional)              MAC_KEY: '<mac>' -            MAC_METHOD_KEY: 'hmac' +            crypto.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 and C{enc_method} is -    EncryptionMethods.AES_256_CTR. +    crypto.EncryptionSchemes.SYMKEY and C{enc_method} is +    crypto.EncryptionMethods.AES_256_CTR.      :param doc_dict: The content of the document to be decrypted.      :type doc_dict: dict @@ -447,27 +467,32 @@ def decrypt_doc_dict(doc_dict, doc_id, doc_rev, key, secret):      :return: The JSON serialization of the decrypted content.      :rtype: str -    :raise UnknownEncryptionMethod: Raised when trying to decrypt from an +    :raise UnknownEncryptionMethodError: Raised when trying to decrypt from an          unknown encryption method.      """      # assert document dictionary structure -    soledad_assert(ENC_JSON_KEY in doc_dict) -    soledad_assert(ENC_SCHEME_KEY in doc_dict) -    soledad_assert(ENC_METHOD_KEY in doc_dict) -    soledad_assert(ENC_IV_KEY in doc_dict) -    soledad_assert(MAC_KEY in doc_dict) -    soledad_assert(MAC_METHOD_KEY in doc_dict) - -    ciphertext = binascii.a2b_hex(doc_dict[ENC_JSON_KEY]) -    enc_scheme = doc_dict[ENC_SCHEME_KEY] -    enc_method = doc_dict[ENC_METHOD_KEY] -    enc_iv = doc_dict[ENC_IV_KEY] -    doc_mac = doc_dict[MAC_KEY] -    mac_method = doc_dict[MAC_METHOD_KEY] - -    soledad_assert(enc_scheme == EncryptionSchemes.SYMKEY) - -    _verify_doc_mac(doc_id, doc_rev, ciphertext, mac_method, secret, doc_mac) +    expected_keys = set([ +        crypto.ENC_JSON_KEY, +        crypto.ENC_SCHEME_KEY, +        crypto.ENC_METHOD_KEY, +        crypto.ENC_IV_KEY, +        crypto.MAC_KEY, +        crypto.MAC_METHOD_KEY, +    ]) +    soledad_assert(expected_keys.issubset(set(doc_dict.keys()))) + +    ciphertext = binascii.a2b_hex(doc_dict[crypto.ENC_JSON_KEY]) +    enc_scheme = doc_dict[crypto.ENC_SCHEME_KEY] +    enc_method = doc_dict[crypto.ENC_METHOD_KEY] +    enc_iv = doc_dict[crypto.ENC_IV_KEY] +    doc_mac = doc_dict[crypto.MAC_KEY] +    mac_method = doc_dict[crypto.MAC_METHOD_KEY] + +    soledad_assert(enc_scheme == crypto.EncryptionSchemes.SYMKEY) + +    _verify_doc_mac( +        doc_id, doc_rev, ciphertext, enc_scheme, enc_method, +        enc_iv, mac_method, secret, doc_mac)      return decrypt_sym(ciphertext, key, method=enc_method, iv=enc_iv) @@ -481,8 +506,9 @@ def is_symmetrically_encrypted(doc):      :rtype: bool      """ -    if doc.content and ENC_SCHEME_KEY in doc.content: -        if doc.content[ENC_SCHEME_KEY] == EncryptionSchemes.SYMKEY: +    if doc.content and crypto.ENC_SCHEME_KEY in doc.content: +        if doc.content[crypto.ENC_SCHEME_KEY] \ +                == crypto.EncryptionSchemes.SYMKEY:              return True      return False diff --git a/client/src/leap/soledad/client/secrets.py b/client/src/leap/soledad/client/secrets.py index b1c22371..970ac82f 100644 --- a/client/src/leap/soledad/client/secrets.py +++ b/client/src/leap/soledad/client/secrets.py @@ -33,33 +33,12 @@ from hashlib import sha256  import simplejson as json -from leap.soledad.common import ( -    soledad_assert, -    soledad_assert_type -) -from leap.soledad.common.document import SoledadDocument -from leap.soledad.common.crypto import ( -    MacMethods, -    UnknownMacMethod, -    WrongMac, -    MAC_KEY, -    MAC_METHOD_KEY, -) -from leap.soledad.common.errors import ( -    InvalidTokenError, -    NotLockedError, -    AlreadyLockedError, -    LockTimedOutError, -) -from leap.soledad.client.events import ( -    SOLEDAD_CREATING_KEYS, -    SOLEDAD_DONE_CREATING_KEYS, -    SOLEDAD_DOWNLOADING_KEYS, -    SOLEDAD_DONE_DOWNLOADING_KEYS, -    SOLEDAD_UPLOADING_KEYS, -    SOLEDAD_DONE_UPLOADING_KEYS, -    signal, -) +from leap.soledad.common import soledad_assert +from leap.soledad.common import soledad_assert_type +from leap.soledad.common import document +from leap.soledad.common import errors +from leap.soledad.common import crypto +from leap.soledad.client import events  logger = logging.getLogger(name=__name__) @@ -227,9 +206,9 @@ class SoledadSecrets(object):              token = timeout = None              try:                  token, timeout = self._shared_db.lock() -            except AlreadyLockedError: +            except errors.AlreadyLockedError:                  raise BootstrapSequenceError('Database is already locked.') -            except LockTimedOutError: +            except errors.LockTimedOutError:                  raise BootstrapSequenceError('Lock operation timed out.')              self._get_or_gen_crypto_secrets() @@ -238,12 +217,12 @@ class SoledadSecrets(object):              try:                  self._shared_db.unlock(token)                  self._shared_db.close() -            except NotLockedError: +            except errors.NotLockedError:                  # for some reason the lock expired. Despite that, secret                  # loading or generation/storage must have been executed                  # successfully, so we pass.                  pass -            except InvalidTokenError: +            except errors.InvalidTokenError:                  # here, our lock has not only expired but also some other                  # client application has obtained a new lock and is currently                  # doing its thing in the shared database. Using the same @@ -403,8 +382,8 @@ class SoledadSecrets(object):              self.KDF_KEY: self.KDF_SCRYPT,              self.KDF_SALT_KEY: binascii.b2a_base64(salt),              self.KDF_LENGTH_KEY: len(key), -            MAC_METHOD_KEY: MacMethods.HMAC, -            MAC_KEY: hmac.new( +            crypto.MAC_METHOD_KEY: crypto.MacMethods.HMAC, +            crypto.MAC_KEY: hmac.new(                  key,                  json.dumps(encrypted_secrets),                  sha256).hexdigest(), @@ -429,13 +408,13 @@ class SoledadSecrets(object):          soledad_assert(self.STORAGE_SECRETS_KEY in data)          # check mac of the recovery document          mac = None -        if MAC_KEY in data: -            soledad_assert(data[MAC_KEY] is not None) -            soledad_assert(MAC_METHOD_KEY in data) +        if crypto.MAC_KEY in data: +            soledad_assert(data[crypto.MAC_KEY] is not None) +            soledad_assert(crypto.MAC_METHOD_KEY in data)              soledad_assert(self.KDF_KEY in data)              soledad_assert(self.KDF_SALT_KEY in data)              soledad_assert(self.KDF_LENGTH_KEY in data) -            if data[MAC_METHOD_KEY] == MacMethods.HMAC: +            if data[crypto.MAC_METHOD_KEY] == crypto.MacMethods.HMAC:                  key = scrypt.hash(                      self._passphrase_as_string(),                      binascii.a2b_base64(data[self.KDF_SALT_KEY]), @@ -445,10 +424,10 @@ class SoledadSecrets(object):                      json.dumps(data[self.STORAGE_SECRETS_KEY]),                      sha256).hexdigest()              else: -                raise UnknownMacMethod('Unknown MAC method: %s.' % -                                       data[MAC_METHOD_KEY]) -            if mac != data[MAC_KEY]: -                raise WrongMac('Could not authenticate recovery document\'s ' +                raise crypto.UnknownMacMethodError('Unknown MAC method: %s.' % +                                       data[crypto.MAC_METHOD_KEY]) +            if mac != data[crypto.MAC_KEY]: +                raise crypto.WrongMacError('Could not authenticate recovery document\'s '                                 'contents.')          # include secrets in the secret pool.          secret_count = 0 @@ -469,15 +448,15 @@ class SoledadSecrets(object):          database.          :return: a document with encrypted key material in its contents -        :rtype: SoledadDocument +        :rtype: document.SoledadDocument          """ -        signal(SOLEDAD_DOWNLOADING_KEYS, self._uuid) +        events.signal(events.SOLEDAD_DOWNLOADING_KEYS, self._uuid)          db = self._shared_db          if not db:              logger.warning('No shared db found')              return          doc = db.get_doc(self._shared_db_doc_id()) -        signal(SOLEDAD_DONE_DOWNLOADING_KEYS, self._uuid) +        events.signal(events.SOLEDAD_DONE_DOWNLOADING_KEYS, self._uuid)          return doc      def _put_secrets_in_shared_db(self): @@ -495,18 +474,18 @@ class SoledadSecrets(object):          # try to get secrets doc from server, otherwise create it          doc = self._get_secrets_from_shared_db()          if doc is None: -            doc = SoledadDocument( +            doc = document.SoledadDocument(                  doc_id=self._shared_db_doc_id())          # fill doc with encrypted secrets          doc.content = self._export_recovery_document()          # upload secrets to server -        signal(SOLEDAD_UPLOADING_KEYS, self._uuid) +        events.signal(events.SOLEDAD_UPLOADING_KEYS, self._uuid)          db = self._shared_db          if not db:              logger.warning('No shared db found')              return          db.put_doc(doc) -        signal(SOLEDAD_DONE_UPLOADING_KEYS, self._uuid) +        events.signal(events.SOLEDAD_DONE_UPLOADING_KEYS, self._uuid)      #      # Management of secret for symmetric encryption. @@ -618,7 +597,7 @@ class SoledadSecrets(object):          Generate a secret for symmetric encryption and store in a local          encrypted file. -        This method emits the following signals: +        This method emits the following events.signals:              * SOLEDAD_CREATING_KEYS              * SOLEDAD_DONE_CREATING_KEYS @@ -626,13 +605,13 @@ class SoledadSecrets(object):          :return: The id of the generated secret.          :rtype: str          """ -        signal(SOLEDAD_CREATING_KEYS, self._uuid) +        events.signal(events.SOLEDAD_CREATING_KEYS, self._uuid)          # generate random secret          secret = os.urandom(self.GEN_SECRET_LENGTH)          secret_id = sha256(secret).hexdigest()          self._secrets[secret_id] = secret          self._store_secrets() -        signal(SOLEDAD_DONE_CREATING_KEYS, self._uuid) +        events.signal(events.SOLEDAD_DONE_CREATING_KEYS, self._uuid)          return secret_id      def _store_secrets(self): diff --git a/common/src/leap/soledad/common/crypto.py b/common/src/leap/soledad/common/crypto.py index ab05999b..b4f3234f 100644 --- a/common/src/leap/soledad/common/crypto.py +++ b/common/src/leap/soledad/common/crypto.py @@ -35,7 +35,7 @@ class EncryptionSchemes(object):      PUBKEY = 'pubkey' -class UnknownEncryptionScheme(Exception): +class UnknownEncryptionSchemeError(Exception):      """      Raised when trying to decrypt from unknown encryption schemes.      """ @@ -51,7 +51,7 @@ class EncryptionMethods(object):      XSALSA20 = 'xsalsa20' -class UnknownEncryptionMethod(Exception): +class UnknownEncryptionMethodError(Exception):      """      Raised when trying to encrypt/decrypt with unknown method.      """ @@ -66,7 +66,7 @@ class MacMethods(object):      HMAC = 'hmac' -class UnknownMacMethod(Exception): +class UnknownMacMethodError(Exception):      """      Raised when trying to authenticate document's content with unknown MAC      mehtod. @@ -74,7 +74,7 @@ class UnknownMacMethod(Exception):      pass -class WrongMac(Exception): +class WrongMacError(Exception):      """      Raised when failing to authenticate document's contents based on MAC.      """ diff --git a/common/src/leap/soledad/common/tests/__init__.py b/common/src/leap/soledad/common/tests/__init__.py index 3081683b..0ab159fd 100644 --- a/common/src/leap/soledad/common/tests/__init__.py +++ b/common/src/leap/soledad/common/tests/__init__.py @@ -27,9 +27,9 @@ from mock import Mock  from leap.soledad.common.document import SoledadDocument +from leap.soledad.common.crypto import ENC_SCHEME_KEY  from leap.soledad.client import Soledad  from leap.soledad.client.crypto import decrypt_doc_dict -from leap.soledad.client.crypto import ENC_SCHEME_KEY  from leap.common.testing.basetest import BaseLeapTest diff --git a/common/src/leap/soledad/common/tests/test_crypto.py b/common/src/leap/soledad/common/tests/test_crypto.py index 0302a268..f5fb4b7a 100644 --- a/common/src/leap/soledad/common/tests/test_crypto.py +++ b/common/src/leap/soledad/common/tests/test_crypto.py @@ -24,7 +24,13 @@ import binascii  from leap.soledad.client import crypto  from leap.soledad.common.document import SoledadDocument  from leap.soledad.common.tests import BaseSoledadTest -from leap.soledad.common.crypto import WrongMac, UnknownMacMethod +from leap.soledad.common.crypto import WrongMacError +from leap.soledad.common.crypto import UnknownMacMethodError +from leap.soledad.common.crypto import EncryptionMethods +from leap.soledad.common.crypto import ENC_JSON_KEY +from leap.soledad.common.crypto import ENC_SCHEME_KEY +from leap.soledad.common.crypto import MAC_KEY +from leap.soledad.common.crypto import MAC_METHOD_KEY  class EncryptedSyncTestCase(BaseSoledadTest): @@ -46,8 +52,8 @@ class EncryptedSyncTestCase(BaseSoledadTest):          self.assertNotEqual(              simpledoc, doc1.content,              'incorrect document encryption') -        self.assertTrue(crypto.ENC_JSON_KEY in doc1.content) -        self.assertTrue(crypto.ENC_SCHEME_KEY in doc1.content) +        self.assertTrue(ENC_JSON_KEY in doc1.content) +        self.assertTrue(ENC_SCHEME_KEY in doc1.content)          # decrypt doc          doc1.set_json(crypto.decrypt_doc(self._soledad._crypto, doc1))          self.assertEqual( @@ -149,13 +155,13 @@ class MacAuthTestCase(BaseSoledadTest):          doc.content = simpledoc          # encrypt doc          doc.set_json(crypto.encrypt_doc(self._soledad._crypto, doc)) -        self.assertTrue(crypto.MAC_KEY in doc.content) -        self.assertTrue(crypto.MAC_METHOD_KEY in doc.content) +        self.assertTrue(MAC_KEY in doc.content) +        self.assertTrue(MAC_METHOD_KEY in doc.content)          # mess with MAC -        doc.content[crypto.MAC_KEY] = '1234567890ABCDEF' +        doc.content[MAC_KEY] = '1234567890ABCDEF'          # try to decrypt doc          self.assertRaises( -            WrongMac, +            WrongMacError,              crypto.decrypt_doc, self._soledad._crypto, doc)      def test_decrypt_with_unknown_mac_method_raises(self): @@ -167,13 +173,13 @@ class MacAuthTestCase(BaseSoledadTest):          doc.content = simpledoc          # encrypt doc          doc.set_json(crypto.encrypt_doc(self._soledad._crypto, doc)) -        self.assertTrue(crypto.MAC_KEY in doc.content) -        self.assertTrue(crypto.MAC_METHOD_KEY in doc.content) +        self.assertTrue(MAC_KEY in doc.content) +        self.assertTrue(MAC_METHOD_KEY in doc.content)          # mess with MAC method -        doc.content[crypto.MAC_METHOD_KEY] = 'mymac' +        doc.content[MAC_METHOD_KEY] = 'mymac'          # try to decrypt doc          self.assertRaises( -            UnknownMacMethod, +            UnknownMacMethodError,              crypto.decrypt_doc, self._soledad._crypto, doc) @@ -184,20 +190,20 @@ class SoledadCryptoAESTestCase(BaseSoledadTest):          key = os.urandom(32)          iv, cyphertext = self._soledad._crypto.encrypt_sym(              'data', key, -            method=crypto.EncryptionMethods.AES_256_CTR) +            method=EncryptionMethods.AES_256_CTR)          self.assertTrue(cyphertext is not None)          self.assertTrue(cyphertext != '')          self.assertTrue(cyphertext != 'data')          plaintext = self._soledad._crypto.decrypt_sym(              cyphertext, key, iv=iv, -            method=crypto.EncryptionMethods.AES_256_CTR) +            method=EncryptionMethods.AES_256_CTR)          self.assertEqual('data', plaintext)      def test_decrypt_with_wrong_iv_fails(self):          key = os.urandom(32)          iv, cyphertext = self._soledad._crypto.encrypt_sym(              'data', key, -            method=crypto.EncryptionMethods.AES_256_CTR) +            method=EncryptionMethods.AES_256_CTR)          self.assertTrue(cyphertext is not None)          self.assertTrue(cyphertext != '')          self.assertTrue(cyphertext != 'data') @@ -208,14 +214,14 @@ class SoledadCryptoAESTestCase(BaseSoledadTest):              wrongiv = os.urandom(1) + rawiv[1:]          plaintext = self._soledad._crypto.decrypt_sym(              cyphertext, key, iv=binascii.b2a_base64(wrongiv), -            method=crypto.EncryptionMethods.AES_256_CTR) +            method=EncryptionMethods.AES_256_CTR)          self.assertNotEqual('data', plaintext)      def test_decrypt_with_wrong_key_fails(self):          key = os.urandom(32)          iv, cyphertext = self._soledad._crypto.encrypt_sym(              'data', key, -            method=crypto.EncryptionMethods.AES_256_CTR) +            method=EncryptionMethods.AES_256_CTR)          self.assertTrue(cyphertext is not None)          self.assertTrue(cyphertext != '')          self.assertTrue(cyphertext != 'data') @@ -225,7 +231,7 @@ class SoledadCryptoAESTestCase(BaseSoledadTest):              wrongkey = os.urandom(32)          plaintext = self._soledad._crypto.decrypt_sym(              cyphertext, wrongkey, iv=iv, -            method=crypto.EncryptionMethods.AES_256_CTR) +            method=EncryptionMethods.AES_256_CTR)          self.assertNotEqual('data', plaintext) @@ -236,20 +242,20 @@ class SoledadCryptoXSalsa20TestCase(BaseSoledadTest):          key = os.urandom(32)          iv, cyphertext = self._soledad._crypto.encrypt_sym(              'data', key, -            method=crypto.EncryptionMethods.XSALSA20) +            method=EncryptionMethods.XSALSA20)          self.assertTrue(cyphertext is not None)          self.assertTrue(cyphertext != '')          self.assertTrue(cyphertext != 'data')          plaintext = self._soledad._crypto.decrypt_sym(              cyphertext, key, iv=iv, -            method=crypto.EncryptionMethods.XSALSA20) +            method=EncryptionMethods.XSALSA20)          self.assertEqual('data', plaintext)      def test_decrypt_with_wrong_iv_fails(self):          key = os.urandom(32)          iv, cyphertext = self._soledad._crypto.encrypt_sym(              'data', key, -            method=crypto.EncryptionMethods.XSALSA20) +            method=EncryptionMethods.XSALSA20)          self.assertTrue(cyphertext is not None)          self.assertTrue(cyphertext != '')          self.assertTrue(cyphertext != 'data') @@ -260,14 +266,14 @@ class SoledadCryptoXSalsa20TestCase(BaseSoledadTest):              wrongiv = os.urandom(1) + rawiv[1:]          plaintext = self._soledad._crypto.decrypt_sym(              cyphertext, key, iv=binascii.b2a_base64(wrongiv), -            method=crypto.EncryptionMethods.XSALSA20) +            method=EncryptionMethods.XSALSA20)          self.assertNotEqual('data', plaintext)      def test_decrypt_with_wrong_key_fails(self):          key = os.urandom(32)          iv, cyphertext = self._soledad._crypto.encrypt_sym(              'data', key, -            method=crypto.EncryptionMethods.XSALSA20) +            method=EncryptionMethods.XSALSA20)          self.assertTrue(cyphertext is not None)          self.assertTrue(cyphertext != '')          self.assertTrue(cyphertext != 'data') @@ -277,5 +283,5 @@ class SoledadCryptoXSalsa20TestCase(BaseSoledadTest):              wrongkey = os.urandom(32)          plaintext = self._soledad._crypto.decrypt_sym(              cyphertext, wrongkey, iv=iv, -            method=crypto.EncryptionMethods.XSALSA20) +            method=EncryptionMethods.XSALSA20)          self.assertNotEqual('data', plaintext) diff --git a/common/src/leap/soledad/common/tests/test_soledad.py b/common/src/leap/soledad/common/tests/test_soledad.py index 12bfbc3e..31c02fc4 100644 --- a/common/src/leap/soledad/common/tests/test_soledad.py +++ b/common/src/leap/soledad/common/tests/test_soledad.py @@ -28,7 +28,7 @@ from leap.soledad.common.tests import (  )  from leap import soledad  from leap.soledad.common.document import SoledadDocument -from leap.soledad.common.crypto import WrongMac +from leap.soledad.common.crypto import WrongMacError  from leap.soledad.client import Soledad  from leap.soledad.client.sqlcipher import SQLCipherDatabase  from leap.soledad.client.secrets import PassphraseTooShort @@ -117,7 +117,7 @@ class AuxMethodsTestCase(BaseSoledadTest):          sol.close()          self.assertRaises( -            WrongMac, +            WrongMacError,              self._soledad_instance, 'leap@leap.se',              passphrase=u'123',              prefix=self.rand_prefix) @@ -208,7 +208,7 @@ class SoledadSignalingTestCase(BaseSoledadTest):      def setUp(self):          # mock signaling          soledad.client.signal = Mock() -        soledad.client.secrets.signal = Mock() +        soledad.client.secrets.events.signal = Mock()          # run parent's setUp          BaseSoledadTest.setUp(self) @@ -230,57 +230,57 @@ class SoledadSignalingTestCase(BaseSoledadTest):            - downloading keys / done downloading keys.            - uploading keys / done uploading keys.          """ -        soledad.client.secrets.signal.reset_mock() +        soledad.client.secrets.events.signal.reset_mock()          # get a fresh instance so it emits all bootstrap signals          sol = self._soledad_instance(              secrets_path='alternative_stage3.json',              local_db_path='alternative_stage3.u1db')          # reverse call order so we can verify in the order the signals were          # expected -        soledad.client.secrets.signal.mock_calls.reverse() -        soledad.client.secrets.signal.call_args = \ -            soledad.client.secrets.signal.call_args_list[0] -        soledad.client.secrets.signal.call_args_list.reverse() +        soledad.client.secrets.events.signal.mock_calls.reverse() +        soledad.client.secrets.events.signal.call_args = \ +            soledad.client.secrets.events.signal.call_args_list[0] +        soledad.client.secrets.events.signal.call_args_list.reverse()          # downloading keys signals -        soledad.client.secrets.signal.assert_called_with( +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DOWNLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DONE_DOWNLOADING_KEYS,              ADDRESS,          )          # creating keys signals -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_CREATING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DONE_CREATING_KEYS,              ADDRESS,          )          # downloading once more (inside _put_keys_in_shared_db) -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DOWNLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DONE_DOWNLOADING_KEYS,              ADDRESS,          )          # uploading keys signals -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_UPLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DONE_UPLOADING_KEYS,              ADDRESS,          ) @@ -312,7 +312,7 @@ class SoledadSignalingTestCase(BaseSoledadTest):          sol.close()          # reset mock -        soledad.client.secrets.signal.reset_mock() +        soledad.client.secrets.events.signal.reset_mock()          # get a fresh instance so it emits all bootstrap signals          sol = self._soledad_instance(              secrets_path='alternative_stage2.json', @@ -320,17 +320,17 @@ class SoledadSignalingTestCase(BaseSoledadTest):              shared_db_class=Stage2MockSharedDB)          # reverse call order so we can verify in the order the signals were          # expected -        soledad.client.secrets.signal.mock_calls.reverse() -        soledad.client.secrets.signal.call_args = \ -            soledad.client.secrets.signal.call_args_list[0] -        soledad.client.secrets.signal.call_args_list.reverse() +        soledad.client.secrets.events.signal.mock_calls.reverse() +        soledad.client.secrets.events.signal.call_args = \ +            soledad.client.secrets.events.signal.call_args_list[0] +        soledad.client.secrets.events.signal.call_args_list.reverse()          # assert download keys signals -        soledad.client.secrets.signal.assert_called_with( +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DOWNLOADING_KEYS,              ADDRESS,          ) -        self._pop_mock_call(soledad.client.secrets.signal) -        soledad.client.secrets.signal.assert_called_with( +        self._pop_mock_call(soledad.client.secrets.events.signal) +        soledad.client.secrets.events.signal.assert_called_with(              proto.SOLEDAD_DONE_DOWNLOADING_KEYS,              ADDRESS,          ) | 
