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

View file

@ -4,8 +4,8 @@
import base64
import os
from Crypto import Random
from Crypto.Cipher import AES
from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes
from cryptography.hazmat.backends import default_backend
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
later use it to decrypt the password and connect to the LDAP server with it.
"""
# 16 bytes of key => AES-128
random = Random.new()
key = os.urandom(16) # random.read(16)
key = os.urandom(16) # 128-bit AES key
iv = os.urandom(16) # 128-bit IV
# initialization vector
iv = os.urandom(16) # random.read(16)
cipher = Cipher(algorithms.AES(key), modes.CFB(iv), backend=default_backend())
encryptor = cipher.encryptor()
# do the encryption
aes = AES.new(key, AES.MODE_CFB, iv)
message = iv + aes.encrypt(cleartext_pw.encode())
ciphertext = encryptor.update(cleartext_pw.encode()) + encryptor.finalize()
message = iv + ciphertext
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_key = base64.b64decode(key)
# first 16 bytes of the message are the initialization vector
iv = decoded_message[:16]
# the rest is the encrypted password
ciphertext = decoded_message[16:]
# decrypt it
aes = AES.new(decoded_key, AES.MODE_CFB, iv)
cleartext_pw = aes.decrypt(ciphertext).decode()
return cleartext_pw
cipher = Cipher(algorithms.AES(decoded_key), modes.CFB(iv), backend=default_backend())
decryptor = cipher.decryptor()
cleartext_pw = decryptor.update(ciphertext) + decryptor.finalize()
return cleartext_pw.decode()
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.
"""
import base64
import pytest
from django.test import TestCase
from account.password_encryption import encrypt_ldap_password, \
@ -28,3 +30,37 @@ class PasswordEncryptionTest(TestCase):
message, key = self.encrypt_it()
decrypted = decrypt_ldap_password(message, key)
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)