update dependencies, replace pycrypto with cryptography

This commit is contained in:
smile 2025-04-11 21:44:34 +02:00
parent 2623444b11
commit 35b4313ad1
7 changed files with 109 additions and 45 deletions

1
.gitignore vendored
View file

@ -4,3 +4,4 @@ local_settings.py
build build
*.sqlite3 *.sqlite3
*~ *~
venv*

View file

@ -4,8 +4,8 @@
import base64 import base64
import os import os
from Crypto import Random from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from Crypto.Cipher import AES from cryptography.hazmat.backends import default_backend
ENCRYPTED_LDAP_PASSWORD = 'encrypted_ldap_password' ENCRYPTED_LDAP_PASSWORD = 'encrypted_ldap_password'
@ -18,17 +18,15 @@ def encrypt_ldap_password(cleartext_pw):
The key is supposed to be stored into the 'session_key' cookie field we can The key is supposed to be stored into the 'session_key' cookie field we can
later use it to decrypt the password and connect to the LDAP server with it. later use it to decrypt the password and connect to the LDAP server with it.
""" """
# 16 bytes of key => AES-128 key = os.urandom(16) # 128-bit AES key
random = Random.new() iv = os.urandom(16) # 128-bit IV
key = os.urandom(16) # random.read(16)
# initialization vector cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
iv = os.urandom(16) # random.read(16) encryptor = cipher.encryptor()
# do the encryption ciphertext = encryptor.update(cleartext_pw.encode()) + encryptor.finalize()
aes = AES.new(key, AES.MODE_CFB, iv)
message = iv + aes.encrypt(cleartext_pw.encode()) message = iv + ciphertext
return base64.b64encode(message).decode(), base64.b64encode(key).decode() return base64.b64encode(message).decode(), base64.b64encode(key).decode()
@ -40,16 +38,14 @@ def decrypt_ldap_password(message, key):
decoded_message = base64.b64decode(message) decoded_message = base64.b64decode(message)
decoded_key = base64.b64decode(key) decoded_key = base64.b64decode(key)
# first 16 bytes of the message are the initialization vector
iv = decoded_message[:16] iv = decoded_message[:16]
# the rest is the encrypted password
ciphertext = decoded_message[16:] ciphertext = decoded_message[16:]
# decrypt it cipher = Cipher(algorithms.AES(decoded_key), modes.CFB(iv), backend=default_backend())
aes = AES.new(decoded_key, AES.MODE_CFB, iv) decryptor = cipher.decryptor()
cleartext_pw = aes.decrypt(ciphertext).decode()
return cleartext_pw cleartext_pw = decryptor.update(ciphertext) + decryptor.finalize()
return cleartext_pw.decode()
def store_ldap_password(request, password): def store_ldap_password(request, password):

View file

@ -4,6 +4,8 @@ when you run "manage.py test".
Replace this with more appropriate tests for your application. Replace this with more appropriate tests for your application.
""" """
import base64
import pytest
from django.test import TestCase from django.test import TestCase
from account.password_encryption import encrypt_ldap_password, \ from account.password_encryption import encrypt_ldap_password, \
@ -28,3 +30,37 @@ class PasswordEncryptionTest(TestCase):
message, key = self.encrypt_it() message, key = self.encrypt_it()
decrypted = decrypt_ldap_password(message, key) decrypted = decrypt_ldap_password(message, key)
self.assertEqual(self.TEST_LDAP_PASSWD, decrypted) self.assertEqual(self.TEST_LDAP_PASSWD, decrypted)
@pytest.mark.parametrize("password", [
"simplePassword123",
"pässwörd_mit_üöäß",
"",
" " * 10,
"🔐✨🚀",
])
def test_encrypt_decrypt_roundtrip(password):
encrypted, key = encrypt_ldap_password(password)
encrypted_bytes = base64.b64decode(encrypted)
key_bytes = base64.b64decode(key)
assert isinstance(encrypted, str)
assert isinstance(key, str)
assert len(key_bytes) == 16 # 128-bit AES
decrypted = decrypt_ldap_password(encrypted, key)
assert decrypted == password
def test_decryption_with_wrong_key_should_fail():
password = "correctPassword"
encrypted, key = encrypt_ldap_password(password)
wrong_key_bytes = base64.b64decode(key)
wrong_key_bytes = bytearray(wrong_key_bytes)
wrong_key_bytes[0] ^= 0xFF # Flip first bit
wrong_key = base64.b64encode(bytes(wrong_key_bytes)).decode()
with pytest.raises(Exception):
decrypt_ldap_password(encrypted, wrong_key)

View file

@ -204,11 +204,9 @@ INSTALLED_APPS = (
'django.contrib.staticfiles', 'django.contrib.staticfiles',
'django.contrib.admin', 'django.contrib.admin',
'django.contrib.admindocs', 'django.contrib.admindocs',
# 'jsonrpc', # STUBBED due to django-1.8.4 upgraded "crispy_forms",
'crispy_forms', "crispy_bootstrap4",
# 'cbmi',
'account', 'account',
# 'cbapi_ldap',
) )
# A sample logging configuration. The only tangible logging # A sample logging configuration. The only tangible logging
@ -242,7 +240,8 @@ LOGGING = {
} }
} }
CRISPY_TEMPLATE_PACK = 'bootstrap' CRISPY_TEMPLATE_PACK = "bootstrap4"
CRISPY_FAIL_SILENTLY = False
# c-base specific settings # c-base specific settings
CBASE_LDAP_URL = 'ldaps://lea.cbrp3.c-base.org:389/' CBASE_LDAP_URL = 'ldaps://lea.cbrp3.c-base.org:389/'

5
pytest.ini Normal file
View file

@ -0,0 +1,5 @@
# pytest.ini
[pytest]
DJANGO_SETTINGS_MODULE = cbmi.settings
python_files = tests.py test_*.py *_tests.py

View file

@ -1,7 +1,12 @@
crispy-bootstrap4
cryptography
Django<5 Django<5
django-auth-ldap django-auth-ldap
django-crispy-forms django-crispy-forms
django-bootstrap4
gunicorn gunicorn
pycrypto # pycrypto
passlib passlib
pytest
pytest-django
requests requests

View file

@ -1,45 +1,67 @@
# #
# This file is autogenerated by pip-compile with python 3.9 # This file is autogenerated by pip-compile with Python 3.9
# To update, run: # by the following command:
# #
# pip-compile requirements.in # pip-compile
# #
asgiref==3.5.2 asgiref==3.8.1
# via django # via django
certifi==2022.5.18.1 certifi==2025.1.31
# via requests # via requests
charset-normalizer==2.0.12 cffi==1.17.1
# via cryptography
charset-normalizer==3.4.1
# via requests # via requests
django==4.0.5 # cryptography==41.0.7
# via -r requirements.in
django==4.2.20
# via # via
# -r requirements.in # -r requirements.in
# django-auth-ldap # django-auth-ldap
django-auth-ldap==4.1.0 # django-crispy-forms
django-auth-ldap==5.1.0
# via -r requirements.in # via -r requirements.in
django-crispy-forms==1.14.0 django-crispy-forms==2.3
# via -r requirements.in # via -r requirements.in
gunicorn==20.1.0 exceptiongroup==1.2.2
# via pytest
gunicorn==23.0.0
# via -r requirements.in # via -r requirements.in
idna==3.3 idna==3.10
# via requests # via requests
iniconfig==2.1.0
# via pytest
packaging==24.2
# via
# gunicorn
# pytest
passlib==1.7.4 passlib==1.7.4
# via -r requirements.in # via -r requirements.in
pyasn1==0.4.8 pluggy==1.5.0
# via pytest
pyasn1==0.6.1
# via # via
# pyasn1-modules # pyasn1-modules
# python-ldap # python-ldap
pyasn1-modules==0.2.8 pyasn1-modules==0.4.2
# via python-ldap # via python-ldap
pycrypto==2.6.1 pycparser==2.22
# via cffi
pytest==8.3.5
# via
# -r requirements.in
# pytest-django
pytest-django==4.11.1
# via -r requirements.in # via -r requirements.in
python-ldap==3.4.0 python-ldap==3.4.4
# via django-auth-ldap # via django-auth-ldap
requests==2.28.0 requests==2.32.3
# via -r requirements.in # via -r requirements.in
sqlparse==0.4.2 sqlparse==0.5.3
# via django # via django
urllib3==1.26.9 tomli==2.2.1
# via pytest
typing-extensions==4.13.2
# via asgiref
urllib3==2.4.0
# via requests # via requests
# The following packages are considered to be unsafe in a requirements file:
# setuptools