Commit 2f838d7a authored by Kazuhiko Shiozaki's avatar Kazuhiko Shiozaki

introduce AES cipher for much stronger encryption. the old implementation...

introduce AES cipher for much stronger encryption. the old implementation (Cesar cipher) is still kept for backward compatibility, but it is recommended to switch to AES cipher.


git-svn-id: https://svn.erp5.org/repos/public/erp5/trunk@43825 20353a03-c40f-0410-a6d1-a30d3c3de9de
parent 11351205
...@@ -55,6 +55,70 @@ from Products.ERP5Security.ERP5UserManager import ERP5UserManager,\ ...@@ -55,6 +55,70 @@ from Products.ERP5Security.ERP5UserManager import ERP5UserManager,\
SUPER_USER,\ SUPER_USER,\
_AuthenticationFailure _AuthenticationFailure
from Crypto.Cipher import AES
from base64 import b16decode, b16encode
class AESCipher:
mode = AES.MODE_CFB
def __init__(self, encryption_key):
# AES key must be either 16, 24, or 32 bytes long
self.encryption_key = encryption_key.ljust(32)[:32]
def encrypt(self, login):
encryptor = AES.new(self.encryption_key, self.mode)
return b16encode(encryptor.encrypt(login.ljust(((len(login)-1)/16+1)*16)))
def decrypt(self, crypted_login):
decryptor = AES.new(self.encryption_key, self.mode)
return decryptor.decrypt(b16decode(crypted_login)).rstrip()
# This cipher is weak. Do not use.
class CesarCipher:
block_length = 3
def __init__(self, encryption_key):
self.encryption_key = encryption_key
self.encrypted_key = self.transformKey(self.encryption_key);
def transformKey(self,key):
"""Transform the key to number for encryption"""
encrypt_key = []
for letter in key:
encrypt_key.append(ord(letter))
return encrypt_key
def encrypt(self, login):
crypted_login = ''
key_length = len(self.encrypted_key)
for i in range(0,len(login)):
delta = i % key_length
crypted_letter = str(ord(login[i]) + self.encrypted_key[delta])
#ord is the inverse of chr() for 8-bit (1111 1111 = 256)
#so crypted_letter max id 512
#we ajust lenght to be able to decrypt by block
crypted_letter = crypted_letter.rjust(self.block_length, '0')
crypted_login += crypted_letter
return crypted_login
def decrypt(self, crypted_login):
login = ''
#check lenght of the string
clogin_length = len(crypted_login)
if clogin_length % self.block_length != 0:
raise ValueError, "Lenght is not good"
#decrypt block per block
position = 0
key_length = len(self.encrypted_key)
for block in range(0, clogin_length, self.block_length):
delta = position % key_length
crypted_letter = crypted_login[block:block + self.block_length]
crypted_letter = int(crypted_letter) - self.encrypted_key[delta]
letter = chr(crypted_letter)
login += letter
position += 1
return login
class ILoginEncryptionPlugin(Interface): class ILoginEncryptionPlugin(Interface):
"""Contract for possible ERP5 Key Auth Plugin""" """Contract for possible ERP5 Key Auth Plugin"""
...@@ -70,12 +134,14 @@ manage_addERP5KeyAuthPluginForm = PageTemplateFile( ...@@ -70,12 +134,14 @@ manage_addERP5KeyAuthPluginForm = PageTemplateFile(
'www/ERP5Security_addERP5KeyAuthPlugin', globals(), 'www/ERP5Security_addERP5KeyAuthPlugin', globals(),
__name__='manage_addERP5KeyAuthPluginForm') __name__='manage_addERP5KeyAuthPluginForm')
def addERP5KeyAuthPlugin(dispatcher, id,title=None,\ def addERP5KeyAuthPlugin(dispatcher, id, title=None,\
encryption_key='',cookie_name='',\ encryption_key='', cipher='AES', cookie_name='',\
default_cookie_name='',REQUEST=None): default_cookie_name='',REQUEST=None):
""" Add a ERP5KeyAuthPlugin to a Pluggable Auth Service. """ """ Add a ERP5KeyAuthPlugin to a Pluggable Auth Service. """
plugin = ERP5KeyAuthPlugin( id, title,encryption_key,cookie_name,default_cookie_name) plugin = ERP5KeyAuthPlugin(id=id, title=title, encryption_key=encryption_key,
cipher=cipher, cookie_name=cookie_name,
default_cookie_name=default_cookie_name)
dispatcher._setObject(plugin.getId(), plugin) dispatcher._setObject(plugin.getId(), plugin)
if REQUEST is not None: if REQUEST is not None:
...@@ -103,11 +169,9 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -103,11 +169,9 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
meta_type = "ERP5 Key Authentication" meta_type = "ERP5 Key Authentication"
login_path = 'login_form' login_path = 'login_form'
security = ClassSecurityInfo() security = ClassSecurityInfo()
block_length = 3
cookie_name = "__ac_key" cookie_name = "__ac_key"
default_cookie_name = "__ac" default_cookie_name = "__ac"
encryption_key = '' encryption_key = ''
encrypted_key = ''
manage_options = ( ( { 'label': 'Edit', manage_options = ( ( { 'label': 'Edit',
'action': 'manage_editERP5KeyAuthPluginForm', } 'action': 'manage_editERP5KeyAuthPluginForm', }
...@@ -127,7 +191,8 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -127,7 +191,8 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
+ CookieAuthHelper._properties[:] + CookieAuthHelper._properties[:]
) )
def __init__(self, id, title=None, encryption_key='', cookie_name='', default_cookie_name=''): def __init__(self, id, title=None, encryption_key='', cipher='AES',
cookie_name='', default_cookie_name=''):
#Check parameters #Check parameters
if cookie_name is None or cookie_name == '': if cookie_name is None or cookie_name == '':
cookie_name = id cookie_name = id
...@@ -142,56 +207,27 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -142,56 +207,27 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
self.cookie_name = cookie_name self.cookie_name = cookie_name
self.default_cookie_name = default_cookie_name self.default_cookie_name = default_cookie_name
self.encryption_key = encryption_key self.encryption_key = encryption_key
self.encrypted_key = self.transformKey(self.encryption_key); self.cipher = cipher
def _getCipher(self):
# If self.cipher does not exist, we use CesarCipher only for
# backward compatibility.
return getattr(self, 'cipher', 'Cesar')
################################ ################################
# ILoginEncryptionPlugin # # ILoginEncryptionPlugin #
################################ ################################
security.declarePrivate('transformKey')
def transformKey(self,key):
"""Transform the key to number for encryption"""
encrypt_key = []
for letter in key:
encrypt_key.append(ord(letter))
return encrypt_key
security.declarePublic('encrypt') security.declarePublic('encrypt')
def encrypt(self,login): def encrypt(self,login):
"""Encrypt the login""" """Encrypt the login"""
crypted_login = '' cipher = globals()['%sCipher' % self._getCipher()](self.encryption_key)
key_length = len(self.encrypted_key) return cipher.encrypt(login)
for i in range(0,len(login)):
delta = i % key_length
crypted_letter = str(ord(login[i]) + self.encrypted_key[delta])
#ord is the inverse of chr() for 8-bit (1111 1111 = 256)
#so crypted_letter max id 512
#we ajust lenght to be able to decrypt by block
crypted_letter = crypted_letter.rjust(self.block_length, '0')
crypted_login += crypted_letter
return crypted_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"""
login = '' cipher = globals()['%sCipher' % self._getCipher()](self.encryption_key)
#check lenght of the string return cipher.decrypt(crypted_login)
clogin_length = len(crypted_login)
if clogin_length % self.block_length != 0:
raise ValueError, "Lenght is not good"
#decrypt block per block
position = 0
key_length = len(self.encrypted_key)
for block in range(0, clogin_length, self.block_length):
delta = position % key_length
crypted_letter = crypted_login[block:block + self.block_length]
crypted_letter = int(crypted_letter) - self.encrypted_key[delta]
letter = chr(crypted_letter)
login += letter
position += 1
return login
#################################### ####################################
#ILoginPasswordHostExtractionPlugin# #ILoginPasswordHostExtractionPlugin#
...@@ -360,7 +396,8 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -360,7 +396,8 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
__name__='manage_editERP5KeyAuthPluginForm' ) __name__='manage_editERP5KeyAuthPluginForm' )
security.declareProtected( ManageUsers, 'manage_editKeyAuthPlugin' ) security.declareProtected( ManageUsers, 'manage_editKeyAuthPlugin' )
def manage_editKeyAuthPlugin(self, encryption_key,cookie_name,default_cookie_name, RESPONSE=None): def manage_editKeyAuthPlugin(self, encryption_key, cipher, cookie_name,
default_cookie_name, RESPONSE=None):
"""Edit the object""" """Edit the object"""
error_message = '' error_message = ''
...@@ -373,7 +410,12 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper): ...@@ -373,7 +410,12 @@ class ERP5KeyAuthPlugin(ERP5UserManager, CookieAuthHelper):
error_message += 'Invalid key value ' error_message += 'Invalid key value '
else: else:
self.encryption_key = encryption_key self.encryption_key = encryption_key
self.encrypted_key = self.transformKey(self.encryption_key);
#Save cipher
if cipher == '' or cipher is None:
error_message += 'Invalid cipher value '
else:
self.cipher = cipher
#Save cookie name #Save cookie name
if cookie_name == '' or cookie_name is None: if cookie_name == '' or cookie_name is None:
......
...@@ -36,6 +36,20 @@ ...@@ -36,6 +36,20 @@
<input type="text" name="encryption_key" size="40" /> <input type="text" name="encryption_key" size="40" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Cipher
</div>
</td>
<td align="left" valign="top">
<select name="cipher">
<tal:block tal:repeat="c python:[('AES', 'AES'), ('Cesar', 'Cesar (DO NOT USE)')]">
<option value="value" tal:attributes="value python:c[0]" tal:content="python:c[1]" />
</tal:block>
</select>
</td>
</tr>
<tr> <tr>
<td align="left" valign="top"> <td align="left" valign="top">
<div class="form-label"> <div class="form-label">
......
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
<form action="manage_editKeyAuthPlugin" method="POST"> <form action="manage_editKeyAuthPlugin" method="POST">
<table tal:define="encryption_key request/encryption_key|context/encryption_key|string:; <table tal:define="encryption_key request/encryption_key|context/encryption_key|string:;
cipher request/cipher|context/cipher|string:Cesar;
default_cookie_name request/default_cookie_name|context/default_cookie_name|string:; default_cookie_name request/default_cookie_name|context/default_cookie_name|string:;
cookie_name request/cookie_name|context/cookie_name|string:;"> cookie_name request/cookie_name|context/cookie_name|string:;">
...@@ -18,6 +19,20 @@ ...@@ -18,6 +19,20 @@
tal:attributes="value encryption_key;" /> tal:attributes="value encryption_key;" />
</td> </td>
</tr> </tr>
<tr>
<td align="left" valign="top">
<div class="form-label">
Cipher
</div>
</td>
<td align="left" valign="top">
<select name="cipher">
<tal:block tal:repeat="c python:[('AES', 'AES'), ('Cesar', 'Cesar (DO NOT USE)')]">
<option value="value" tal:attributes="value python:c[0]; selected python:c[0]==cipher" tal:content="python:c[1]" />
</tal:block>
</select>
</td>
</tr>
<tr> <tr>
<td> Cookie Name </td> <td> Cookie Name </td>
<td> <td>
......
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