diff options
Diffstat (limited to 'soledad/src')
-rw-r--r-- | soledad/src/leap/soledad/crypto.py | 54 | ||||
-rw-r--r-- | soledad/src/leap/soledad/target.py | 2 | ||||
-rw-r--r-- | soledad/src/leap/soledad/tests/test_crypto.py | 54 |
3 files changed, 84 insertions, 26 deletions
diff --git a/soledad/src/leap/soledad/crypto.py b/soledad/src/leap/soledad/crypto.py index bfad66d1..3c1061d5 100644 --- a/soledad/src/leap/soledad/crypto.py +++ b/soledad/src/leap/soledad/crypto.py @@ -27,8 +27,8 @@ import hmac import hashlib -from Crypto.Cipher import AES -from Crypto.Util import Counter +from pycryptopp.cipher.aes import AES +from pycryptopp.cipher.xsalsa20 import XSalsa20 from leap.soledad import ( @@ -43,6 +43,7 @@ class EncryptionMethods(object): """ AES_256_CTR = 'aes-256-ctr' + XSALSA20 = 'xsalsa20' class UnknownEncryptionMethod(Exception): @@ -93,19 +94,23 @@ class SoledadCrypto(object): """ 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 = None # AES-256 in CTR mode if method == EncryptionMethods.AES_256_CTR: - 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(8) - ctr = Counter.new(64, prefix=iv) - cipher = AES.new(key=key, mode=AES.MODE_CTR, counter=ctr) - return binascii.b2a_base64(iv), cipher.encrypt(data) - - # raise if method is unknown - raise UnknownEncryptionMethod('Unkwnown method: %s' % method) + iv = os.urandom(16) + ciphertext = AES(key=key, iv=iv).process(data) + # XSalsa20 + elif method == EncryptionMethods.XSALSA20: + iv = os.urandom(24) + ciphertext = XSalsa20(key=key, iv=iv).process(data) + else: + # raise if method is unknown + raise UnknownEncryptionMethod('Unkwnown method: %s' % method) + return binascii.b2a_base64(iv), ciphertext def decrypt_sym(self, data, key, method=EncryptionMethods.AES_256_CTR, **kwargs): @@ -127,19 +132,20 @@ class SoledadCrypto(object): @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)) + soledad_assert( + 'iv' in kwargs, + '%s needs an initial value.' % method) # AES-256 in CTR mode if method == EncryptionMethods.AES_256_CTR: - # assert params - soledad_assert( - len(key) == 32, # 32 x 8 = 256 bits. - 'Wrong key size: %s (must be 256 bits long).' % len(key)) - soledad_assert( - 'iv' in kwargs, - 'AES-256-CTR needs an initial value.') - ctr = Counter.new(64, prefix=binascii.a2b_base64(kwargs['iv'])) - cipher = AES.new(key=key, mode=AES.MODE_CTR, counter=ctr) - return cipher.decrypt(data) + return AES( + key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) + elif method == EncryptionMethods.XSALSA20: + return XSalsa20( + key=key, iv=binascii.a2b_base64(kwargs['iv'])).process(data) # raise if method is unknown raise UnknownEncryptionMethod('Unkwnown method: %s' % method) diff --git a/soledad/src/leap/soledad/target.py b/soledad/src/leap/soledad/target.py index 9fac9f54..cad51b74 100644 --- a/soledad/src/leap/soledad/target.py +++ b/soledad/src/leap/soledad/target.py @@ -168,7 +168,7 @@ def encrypt_doc(crypto, doc): soledad_assert(doc.is_tombstone() is False) # encrypt content using AES-256 CTR mode iv, ciphertext = crypto.encrypt_sym( - doc.get_json(), + str(doc.get_json()), # encryption/decryption routines expect str crypto.doc_passphrase(doc.doc_id), method=EncryptionMethods.AES_256_CTR) # Return a representation for the encrypted content. In the following, we diff --git a/soledad/src/leap/soledad/tests/test_crypto.py b/soledad/src/leap/soledad/tests/test_crypto.py index c727a2ff..eea67b45 100644 --- a/soledad/src/leap/soledad/tests/test_crypto.py +++ b/soledad/src/leap/soledad/tests/test_crypto.py @@ -189,7 +189,7 @@ class MacAuthTestCase(BaseSoledadTest): target.decrypt_doc, self._soledad._crypto, doc) -class SoledadCryptoTestCase(BaseSoledadTest): +class SoledadCryptoAESTestCase(BaseSoledadTest): def test_encrypt_decrypt_sym(self): # generate 256-bit key @@ -239,3 +239,55 @@ class SoledadCryptoTestCase(BaseSoledadTest): cyphertext, wrongkey, iv=iv, method=crypto.EncryptionMethods.AES_256_CTR) self.assertNotEqual('data', plaintext) + + +class SoledadCryptoXSalsa20TestCase(BaseSoledadTest): + + def test_encrypt_decrypt_sym(self): + # generate 256-bit key + key = Random.new().read(32) + iv, cyphertext = self._soledad._crypto.encrypt_sym( + 'data', key, + method=crypto.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) + self.assertEqual('data', plaintext) + + def test_decrypt_with_wrong_iv_fails(self): + key = Random.new().read(32) + iv, cyphertext = self._soledad._crypto.encrypt_sym( + 'data', key, + method=crypto.EncryptionMethods.XSALSA20) + self.assertTrue(cyphertext is not None) + self.assertTrue(cyphertext != '') + self.assertTrue(cyphertext != 'data') + # get a different iv by changing the first byte + rawiv = binascii.a2b_base64(iv) + wrongiv = rawiv + while wrongiv == rawiv: + wrongiv = os.urandom(1) + rawiv[1:] + plaintext = self._soledad._crypto.decrypt_sym( + cyphertext, key, iv=binascii.b2a_base64(wrongiv), + method=crypto.EncryptionMethods.XSALSA20) + self.assertNotEqual('data', plaintext) + + def test_decrypt_with_wrong_key_fails(self): + key = Random.new().read(32) + iv, cyphertext = self._soledad._crypto.encrypt_sym( + 'data', key, + method=crypto.EncryptionMethods.XSALSA20) + self.assertTrue(cyphertext is not None) + self.assertTrue(cyphertext != '') + self.assertTrue(cyphertext != 'data') + wrongkey = Random.new().read(32) # 256-bits key + # ensure keys are different in case we are extremely lucky + while wrongkey == key: + wrongkey = Random.new().read(32) + plaintext = self._soledad._crypto.decrypt_sym( + cyphertext, wrongkey, iv=iv, + method=crypto.EncryptionMethods.XSALSA20) + self.assertNotEqual('data', plaintext) |