update dependencies, replace pycrypto with cryptography
This commit is contained in:
parent
2623444b11
commit
35b4313ad1
7 changed files with 109 additions and 45 deletions
1
.gitignore
vendored
1
.gitignore
vendored
|
|
@ -4,3 +4,4 @@ local_settings.py
|
||||||
build
|
build
|
||||||
*.sqlite3
|
*.sqlite3
|
||||||
*~
|
*~
|
||||||
|
venv*
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
|
|
||||||
|
cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
|
||||||
|
encryptor = cipher.encryptor()
|
||||||
|
|
||||||
# initialization vector
|
ciphertext = encryptor.update(cleartext_pw.encode()) + encryptor.finalize()
|
||||||
iv = os.urandom(16) # random.read(16)
|
|
||||||
|
|
||||||
# do the encryption
|
message = iv + ciphertext
|
||||||
aes = AES.new(key, AES.MODE_CFB, iv)
|
|
||||||
message = iv + aes.encrypt(cleartext_pw.encode())
|
|
||||||
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):
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -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
5
pytest.ini
Normal file
|
|
@ -0,0 +1,5 @@
|
||||||
|
# pytest.ini
|
||||||
|
[pytest]
|
||||||
|
DJANGO_SETTINGS_MODULE = cbmi.settings
|
||||||
|
python_files = tests.py test_*.py *_tests.py
|
||||||
|
|
||||||
|
|
@ -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
|
||||||
|
|
|
||||||
|
|
@ -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
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue