diff options
Diffstat (limited to 'client/src/leap/soledad/client/crypto.py')
-rw-r--r-- | client/src/leap/soledad/client/crypto.py | 367 |
1 files changed, 0 insertions, 367 deletions
diff --git a/client/src/leap/soledad/client/crypto.py b/client/src/leap/soledad/client/crypto.py index 55c49d9c..ecc0a0cf 100644 --- a/client/src/leap/soledad/client/crypto.py +++ b/client/src/leap/soledad/client/crypto.py @@ -32,370 +32,3 @@ 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.log import getLogger - - -logger = getLogger(__name__) - - -MAC_KEY_LENGTH = 64 - -crypto_backend = MultiBackend([OpenSSLBackend()]) - - -# TODO -- deprecate. -# Secrets still using this. - -def encrypt_sym(data, key): - """ - Encrypt data using AES-256 cipher in CTR mode. - - :param data: The data to be encrypted. - :type data: str - :param key: The key used to encrypt data (must be 256 bits long). - :type key: str - - :return: A tuple with the initialization vector and the encrypted data. - :rtype: (long, str) - """ - soledad_assert_type(key, str) - soledad_assert( - len(key) == 32, # 32 x 8 = 256 bits. - 'Wrong key size: %s bits (must be 256 bits long).' % - (len(key) * 8)) - - iv = os.urandom(16) - cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=crypto_backend) - encryptor = cipher.encryptor() - ciphertext = encryptor.update(data) + encryptor.finalize() - - return binascii.b2a_base64(iv), ciphertext - - -# FIXME decryption of the secrets doc is still using b64 -# Deprecate that, move to hex. - -def decrypt_sym(data, key, iv, encoding='base64'): - """ - Decrypt some data previously encrypted using AES-256 cipher in CTR mode. - - :param data: The data to be decrypted. - :type data: str - :param key: The symmetric key used to decrypt data (must be 256 bits - long). - :type key: str - :param iv: The initialization vector. - :type iv: str (it's b64 encoded by secrets, hex by deserializing from wire) - - :return: The decrypted data. - :rtype: str - """ - soledad_assert_type(key, str) - # assert params - soledad_assert( - len(key) == 32, # 32 x 8 = 256 bits. - 'Wrong key size: %s (must be 256 bits long).' % len(key)) - - if encoding == 'base64': - iv = binascii.a2b_base64(iv) - elif encoding == 'hex': - iv = binascii.a2b_hex(iv) - - cipher = Cipher(algorithms.AES(key), modes.CTR(iv), backend=crypto_backend) - decryptor = cipher.decryptor() - return decryptor.update(data) + decryptor.finalize() - - -def doc_mac_key(doc_id, secret): - """ - 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 - - :param secret: The Soledad storage secret - :type secret: str - - :return: The key. - :rtype: str - """ - soledad_assert(secret is not None) - return hmac.new( - secret[:MAC_KEY_LENGTH], - doc_id, - hashlib.sha256).digest() - - -# -# Crypto utilities for a SoledadDocument. -# - -# TODO should be ported to streaming consumer - -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}. - - 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 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 enc_scheme: The encryption scheme. - :type enc_scheme: bytes - :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 - :type secret: str - - :return: The calculated MAC. - :rtype: str - - :raise crypto.UnknownMacMethodError: Raised when C{mac_method} is unknown. - """ - try: - soledad_assert(mac_method == crypto.MacMethods.HMAC) - except AssertionError: - 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) - - digest = hmac.new( - doc_mac_key(doc_id, secret), - content, - hashlib.sha256).digest() - return digest - - -#def encrypt_docstr(docstr, doc_id, doc_rev, key, secret): - #""" - #Encrypt C{doc}'s content. -# - #Encrypt doc's contents using AES-256 CTR mode and return a valid JSON - #string representing the following: -# - #{ - #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>' - #crypto.MAC_METHOD_KEY: 'hmac' - #} -# - #:param docstr: A representation of the document to be encrypted. - #:type docstr: str or unicode. -# - #:param doc_id: The document id. - #:type doc_id: str -# - #:param doc_rev: The document revision. - #:type doc_rev: str -# - #:param key: The key used to encrypt ``data`` (must be 256 bits long). - #:type key: str -# - #:param secret: The Soledad storage secret (used for MAC auth). - #:type secret: str -# - #:return: The JSON serialization of the dict representing the encrypted - #content. - #:rtype: str - #""" - #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) - #mac = binascii.b2a_hex( # store the mac as hex. - #mac_doc( - #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 - # 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) - #log.debug("Encrypting doc: %s" % doc_id) - #return json.dumps({ - #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, - #}) -# - - -# TODO port to _crypto -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. - - :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 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 - :type secret: str - :param doc_mac: The MAC to be verified against. - :type doc_mac: str - - :raise crypto.UnknownMacMethodError: Raised when C{mac_method} is unknown. - :raise crypto.WrongMacError: Raised when MAC could not be verified. - """ - # TODO mac_doc should be ported to Streaming also - calculated_mac = mac_doc( - doc_id, - doc_rev, - ciphertext, - enc_scheme, - enc_method, - enc_iv, - mac_method, - secret) - - ok = hmac.compare_digest( - str(calculated_mac), - binascii.a2b_hex(doc_mac)) - - if not ok: - loggger.warn("wrong MAC while decrypting doc...") - loggger.info(u'given: %s' % doc_mac) - loggger.info(u'calculated: %s' % binascii.b2a_hex(calculated_mac)) - raise crypto.WrongMacError("Could not authenticate document's " - "contents.") - - -def decrypt_doc_dict(doc_dict, doc_id, doc_rev, key, secret): - """ - Decrypt a symmetrically encrypted C{doc}'s content. - - Return the JSON string representation of the document's decrypted content. - - The passed doc_dict argument should have the following structure: - - { - 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>' - 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 - 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 - - :param doc_id: The document id. - :type doc_id: str - - :param doc_rev: The document revision. - :type doc_rev: str - - :param key: The key used to encrypt ``data`` (must be 256 bits long). - :type key: str - - :param secret: The Soledad storage secret. - :type secret: str - - :return: The JSON serialization of the decrypted content. - :rtype: str - - :raise UnknownEncryptionMethodError: Raised when trying to decrypt from an - unknown encryption method. - """ - # assert document dictionary structure - 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()))) - - d = doc_dict - decode = binascii.a2b_hex - - enc_scheme = d[crypto.ENC_SCHEME_KEY] - enc_method = d[crypto.ENC_METHOD_KEY] - doc_mac = d[crypto.MAC_KEY] - mac_method = d[crypto.MAC_METHOD_KEY] - enc_iv = d[crypto.ENC_IV_KEY] - - ciphertext_hex = d[crypto.ENC_JSON_KEY] - ciphertext = decode(ciphertext_hex) - - 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) - - decr = decrypt_sym(ciphertext, key, enc_iv, encoding='hex') - return decr - - -# TODO deprecate -def is_symmetrically_encrypted(doc): - """ - Return True if the document was symmetrically encrypted. - - :param doc: The document to check. - :type doc: SoledadDocument - - :rtype: bool - """ - 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 |