summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorTomás Touceda <chiiph@leap.se>2013-08-15 11:33:26 -0300
committerTomás Touceda <chiiph@leap.se>2013-08-15 11:33:26 -0300
commita9ddf49a05e3f6610dd5a96307c37fb4f4cf3d65 (patch)
tree24d4626674968746854d9d9e0cb21dc3126429c1
parent5513cbb2432255087cfff47f1a287cdd351b892e (diff)
parentdf8b3c9bcbb3659667f8a7e08de6ad9aaf527ff2 (diff)
Merge remote-tracking branch 'drebs/feature/2601-use-pycryptopp' into develop
-rw-r--r--changes/feature_add-xsalsa20-encryption-method1
-rw-r--r--changes/feature_use-pycryptopp-for-symmetric-encryption1
-rw-r--r--soledad/setup.py4
-rw-r--r--soledad/src/leap/soledad/crypto.py54
-rw-r--r--soledad/src/leap/soledad/target.py2
-rw-r--r--soledad/src/leap/soledad/tests/test_crypto.py54
6 files changed, 88 insertions, 28 deletions
diff --git a/changes/feature_add-xsalsa20-encryption-method b/changes/feature_add-xsalsa20-encryption-method
new file mode 100644
index 00000000..66b97de8
--- /dev/null
+++ b/changes/feature_add-xsalsa20-encryption-method
@@ -0,0 +1 @@
+ o Add XSalsa20 symmetric encryption method.
diff --git a/changes/feature_use-pycryptopp-for-symmetric-encryption b/changes/feature_use-pycryptopp-for-symmetric-encryption
new file mode 100644
index 00000000..1f28db97
--- /dev/null
+++ b/changes/feature_use-pycryptopp-for-symmetric-encryption
@@ -0,0 +1 @@
+ o Use pycryptopp for symmetric encryption.
diff --git a/soledad/setup.py b/soledad/setup.py
index f2291662..6da976a9 100644
--- a/soledad/setup.py
+++ b/soledad/setup.py
@@ -32,8 +32,7 @@ install_requirements = [
'six==1.1.0',
'scrypt',
'pyxdg',
- 'pycrypto',
- 'pyOpenSSL',
+ 'pycryptopp',
]
@@ -43,6 +42,7 @@ tests_requirements = [
'testscenarios',
'leap.common',
'leap.soledad_server',
+ 'pyOpenSSL',
]
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)