Commit 9964c13d authored by Jérome Perrin's avatar Jérome Perrin

ERP5Security/ERP5KeyAuthPlugin: replace pycrypto by cryptography

pycrypto is unmaintained and the python3 version no longer work with
buildout 3 (it uses a tricky way to run 2to3 in setup.py that no longer
happens with pip based buildout3)

Also make the rest of this file python3 compatible.
parent 1e371494
...@@ -27,12 +27,21 @@ ...@@ -27,12 +27,21 @@
# #
############################################################################## ##############################################################################
from base64 import encodestring, decodestring import six
if six.PY2:
from base64 import encodestring as encodebytes, decodestring as decodebytes
else:
from base64 import encodebytes, decodebytes
from base64 import urlsafe_b64decode, urlsafe_b64encode
from six.moves.urllib.parse import quote, unquote from six.moves.urllib.parse import quote, unquote
from DateTime import DateTime from DateTime import DateTime
from zLOG import LOG, PROBLEM from zLOG import LOG, PROBLEM
from Products.ERP5Type.Globals import InitializeClass from Products.ERP5Type.Globals import InitializeClass
from zope.interface import Interface from zope.interface import Interface
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.primitives import padding
from cryptography.hazmat.backends import default_backend
import os
from AccessControl import ClassSecurityInfo from AccessControl import ClassSecurityInfo
...@@ -46,31 +55,45 @@ from Products.PluggableAuthService.plugins.CookieAuthHelper import CookieAuthHel ...@@ -46,31 +55,45 @@ from Products.PluggableAuthService.plugins.CookieAuthHelper import CookieAuthHel
from Products.ERP5Type.Cache import CachingMethod from Products.ERP5Type.Cache import CachingMethod
from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod from Products.ERP5Type.UnrestrictedMethod import UnrestrictedMethod
from Products.ERP5Type.Utils import bytes2str, str2bytes
from Products.ERP5Security.ERP5UserManager import ERP5UserManager, \ from Products.ERP5Security.ERP5UserManager import ERP5UserManager, \
_AuthenticationFailure _AuthenticationFailure
from Products import ERP5Security from Products import ERP5Security
from Crypto.Cipher import AES
from Crypto import Random
from base64 import urlsafe_b64decode, urlsafe_b64encode
class AESCipher: class AESCipher:
mode = AES.MODE_CFB
def __init__(self, encryption_key): def __init__(self, encryption_key):
# AES key must be either 16, 24, or 32 bytes long # AES key must be either 16, 24, or 32 bytes long
self.encryption_key = encryption_key.ljust(32)[:32] self.encryption_key = str2bytes(encryption_key).ljust(32)[:32]
def encrypt(self, login): def encrypt(self, login):
iv = Random.new().read(AES.block_size) iv = os.urandom(16)
encryptor = AES.new(self.encryption_key, self.mode, IV=iv) cipher = Cipher(
return urlsafe_b64encode(iv + encryptor.encrypt(login.ljust(((len(login)-1)/16+1)*16))) algorithms.AES(self.encryption_key),
modes.CBC(iv),
backend=default_backend())
encryptor = cipher.encryptor()
padder = padding.PKCS7(algorithms.AES.block_size).padder()
padded_data = padder.update(str2bytes(login)) + padder.finalize()
ct = encryptor.update(padded_data) + encryptor.finalize()
return urlsafe_b64encode(iv + ct)
def decrypt(self, crypted_login): def decrypt(self, crypted_login):
decoded_crypted_login = urlsafe_b64decode(crypted_login) decoded_crypted_login = urlsafe_b64decode(crypted_login)
iv = decoded_crypted_login[:AES.block_size] iv = decoded_crypted_login[:16]
decryptor = AES.new(self.encryption_key, self.mode, IV=iv) ct = decoded_crypted_login[16:]
return decryptor.decrypt(decoded_crypted_login[AES.block_size:]).rstrip() cipher = Cipher(algorithms.AES(self.encryption_key), modes.CBC(iv), backend=default_backend())
decryptor = cipher.decryptor()
padded_data = decryptor.update(ct) + decryptor.finalize()
unpadder = padding.PKCS7(algorithms.AES.block_size).unpadder()
data = unpadder.update(padded_data) + unpadder.finalize()
return data
# This cipher is weak. Do not use. # This cipher is weak. Do not use.
class CesarCipher: class CesarCipher:
...@@ -217,13 +240,13 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -217,13 +240,13 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
def encrypt(self, login): def encrypt(self, login):
"""Encrypt the login""" """Encrypt the login"""
cipher = globals()['%sCipher' % self._getCipher()](self.encryption_key) cipher = globals()['%sCipher' % self._getCipher()](self.encryption_key)
return cipher.encrypt(login) return bytes2str(cipher.encrypt(login))
security.declarePrivate('decrypt') security.declarePrivate('decrypt')
def decrypt(self, crypted_login): def decrypt(self, crypted_login):
"""Decrypt string and return the login""" """Decrypt string and return the login"""
cipher = globals()['%sCipher' % self._getCipher()](self.encryption_key) cipher = globals()['%sCipher' % self._getCipher()](self.encryption_key)
return cipher.decrypt(crypted_login) return bytes2str(cipher.decrypt(crypted_login))
#################################### ####################################
#ILoginPasswordHostExtractionPlugin# #ILoginPasswordHostExtractionPlugin#
...@@ -263,7 +286,7 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -263,7 +286,7 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
default_cookie = request.get(self.default_cookie_name, None) default_cookie = request.get(self.default_cookie_name, None)
if default_cookie is not None: if default_cookie is not None:
#Cookie is found #Cookie is found
cookie_val = decodestring(unquote(default_cookie)) cookie_val = decodebytes(unquote(default_cookie))
if cookie_val is not None: if cookie_val is not None:
login, password = cookie_val.split(':') login, password = cookie_val.split(':')
creds['login'] = login creds['login'] = login
...@@ -298,7 +321,7 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -298,7 +321,7 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
response.setCookie(self.cookie_name, quote(cookie_val), path='/')#, expires=expires) response.setCookie(self.cookie_name, quote(cookie_val), path='/')#, expires=expires)
response.expireCookie(self.default_cookie_name, path='/') response.expireCookie(self.default_cookie_name, path='/')
elif login is not None and new_password is not None: elif login is not None and new_password is not None:
cookie_val = encodestring('%s:%s' % (login, new_password)) cookie_val = encodebytes('%s:%s' % (login, new_password))
cookie_val = cookie_val.rstrip() cookie_val = cookie_val.rstrip()
response.setCookie(self.default_cookie_name, quote(cookie_val), path='/') response.setCookie(self.default_cookie_name, quote(cookie_val), path='/')
response.expireCookie(self.cookie_name, path='/') response.expireCookie(self.cookie_name, path='/')
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment