From 0bdd3a6ac281a8501f43600fbf0d8e79f52b5c21 Mon Sep 17 00:00:00 2001 From: Oscar Krause Date: Mon, 17 Mar 2025 14:05:26 +0100 Subject: [PATCH] migrated from "pycryptodome" to "cryptography" --- .DEBIAN/control | 2 +- .DEBIAN/requirements-bookworm-12.txt | 4 +- .DEBIAN/requirements-ubuntu-24.04.txt | 4 +- .DEBIAN/requirements-ubuntu-24.10.txt | 4 +- .PKGBUILD/PKGBUILD | 2 +- app/main.py | 16 ++++---- app/util.py | 56 ++++++++++++++++++--------- requirements.txt | 4 +- test/main.py | 10 ++--- 9 files changed, 61 insertions(+), 41 deletions(-) diff --git a/.DEBIAN/control b/.DEBIAN/control index c2f6ccd..b58c41c 100644 --- a/.DEBIAN/control +++ b/.DEBIAN/control @@ -2,7 +2,7 @@ Package: fastapi-dls Version: 0.0 Architecture: all Maintainer: Oscar Krause oscar.krause@collinwebdesigns.de -Depends: python3, python3-fastapi, python3-uvicorn, python3-dotenv, python3-dateutil, python3-josepy, python3-sqlalchemy, python3-pycryptodome, python3-markdown, uvicorn, openssl +Depends: python3, python3-fastapi, python3-uvicorn, python3-dotenv, python3-dateutil, python3-josepy, python3-sqlalchemy, python3-cryptography, python3-markdown, uvicorn, openssl Recommends: curl Installed-Size: 10240 Homepage: https://git.collinwebdesigns.de/oscar.krause/fastapi-dls diff --git a/.DEBIAN/requirements-bookworm-12.txt b/.DEBIAN/requirements-bookworm-12.txt index 223c64c..c3fe52e 100644 --- a/.DEBIAN/requirements-bookworm-12.txt +++ b/.DEBIAN/requirements-bookworm-12.txt @@ -1,8 +1,8 @@ # https://packages.debian.org/hu/ fastapi==0.92.0 uvicorn[standard]==0.17.6 -python-jose[pycryptodome]==3.3.0 -pycryptodome==3.11.0 +python-jose[cryptography]==3.3.0 +cryptography==38.0.4 python-dateutil==2.8.2 sqlalchemy==1.4.46 markdown==3.4.1 diff --git a/.DEBIAN/requirements-ubuntu-24.04.txt b/.DEBIAN/requirements-ubuntu-24.04.txt index 7cb653b..0ba3025 100644 --- a/.DEBIAN/requirements-ubuntu-24.04.txt +++ b/.DEBIAN/requirements-ubuntu-24.04.txt @@ -1,8 +1,8 @@ # https://packages.ubuntu.com fastapi==0.101.0 uvicorn[standard]==0.27.1 -python-jose[pycryptodome]==3.3.0 -pycryptodome==3.20.0 +python-jose[cryptography]==3.3.0 +cryptography==41.0.7 python-dateutil==2.8.2 sqlalchemy==1.4.50 markdown==3.5.2 diff --git a/.DEBIAN/requirements-ubuntu-24.10.txt b/.DEBIAN/requirements-ubuntu-24.10.txt index 7a65314..59f9361 100644 --- a/.DEBIAN/requirements-ubuntu-24.10.txt +++ b/.DEBIAN/requirements-ubuntu-24.10.txt @@ -1,8 +1,8 @@ # https://packages.ubuntu.com fastapi==0.110.3 uvicorn[standard]==0.30.3 -python-jose[pycryptodome]==3.3.0 -pycryptodome==3.20.0 +python-jose[cryptography]==3.3.0 +cryptography==42.0.5 python-dateutil==2.9.0 sqlalchemy==2.0.32 markdown==3.6 diff --git a/.PKGBUILD/PKGBUILD b/.PKGBUILD/PKGBUILD index 09f606b..44d36c9 100644 --- a/.PKGBUILD/PKGBUILD +++ b/.PKGBUILD/PKGBUILD @@ -8,7 +8,7 @@ pkgdesc='NVIDIA DLS server implementation with FastAPI' arch=('any') url='https://git.collinwebdesigns.de/oscar.krause/fastapi-dls' license=('MIT') -depends=('python' 'python-jose' 'python-starlette' 'python-httpx' 'python-fastapi' 'python-dotenv' 'python-dateutil' 'python-sqlalchemy' 'python-pycryptodome' 'uvicorn' 'python-markdown' 'openssl') +depends=('python' 'python-jose' 'python-starlette' 'python-httpx' 'python-fastapi' 'python-dotenv' 'python-dateutil' 'python-sqlalchemy' 'python3-cryptography' 'uvicorn' 'python-markdown' 'openssl') provider=("$pkgname") install="$pkgname.install" backup=('etc/default/fastapi-dls') diff --git a/app/main.py b/app/main.py index 850a6a2..60f8c60 100644 --- a/app/main.py +++ b/app/main.py @@ -21,7 +21,7 @@ from starlette.middleware.cors import CORSMiddleware from starlette.responses import StreamingResponse, JSONResponse as JSONr, HTMLResponse as HTMLr, Response, RedirectResponse from orm import Origin, Lease, init as db_init, migrate -from util import load_key, load_file +from util import load_private_key, load_public_key, get_pem, load_file # Load variables load_dotenv('../version.env') @@ -42,8 +42,8 @@ DLS_PORT = int(env('DLS_PORT', '443')) SITE_KEY_XID = str(env('SITE_KEY_XID', '00000000-0000-0000-0000-000000000000')) INSTANCE_REF = str(env('INSTANCE_REF', '10000000-0000-0000-0000-000000000001')) ALLOTMENT_REF = str(env('ALLOTMENT_REF', '20000000-0000-0000-0000-000000000001')) -INSTANCE_KEY_RSA = load_key(str(env('INSTANCE_KEY_RSA', join(dirname(__file__), 'cert/instance.private.pem')))) -INSTANCE_KEY_PUB = load_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__), 'cert/instance.public.pem')))) +INSTANCE_KEY_RSA = load_private_key(str(env('INSTANCE_KEY_RSA', join(dirname(__file__), 'cert/instance.private.pem')))) +INSTANCE_KEY_PUB = load_public_key(str(env('INSTANCE_KEY_PUB', join(dirname(__file__), 'cert/instance.public.pem')))) TOKEN_EXPIRE_DELTA = relativedelta(days=int(env('TOKEN_EXPIRE_DAYS', 1)), hours=int(env('TOKEN_EXPIRE_HOURS', 0))) LEASE_EXPIRE_DELTA = relativedelta(days=int(env('LEASE_EXPIRE_DAYS', 90)), hours=int(env('LEASE_EXPIRE_HOURS', 0))) LEASE_RENEWAL_PERIOD = float(env('LEASE_RENEWAL_PERIOD', 0.15)) @@ -51,8 +51,8 @@ LEASE_RENEWAL_DELTA = timedelta(days=int(env('LEASE_EXPIRE_DAYS', 90)), hours=in CLIENT_TOKEN_EXPIRE_DELTA = relativedelta(years=12) CORS_ORIGINS = str(env('CORS_ORIGINS', '')).split(',') if (env('CORS_ORIGINS')) else [f'https://{DLS_URL}'] -jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) -jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) +jwt_encode_key = jwk.construct(get_pem(INSTANCE_KEY_RSA), algorithm=ALGORITHMS.RS256) +jwt_decode_key = jwk.construct(get_pem(INSTANCE_KEY_PUB), algorithm=ALGORITHMS.RS256) # Logging LOG_LEVEL = logging.DEBUG if DEBUG else logging.INFO @@ -264,10 +264,10 @@ async def _client_token(): }, "service_instance_public_key_configuration": { "service_instance_public_key_me": { - "mod": hex(INSTANCE_KEY_PUB.public_key().n)[2:], - "exp": int(INSTANCE_KEY_PUB.public_key().e), + "mod": hex(INSTANCE_KEY_PUB.public_numbers().n)[2:], + "exp": int(INSTANCE_KEY_PUB.public_numbers().e), }, - "service_instance_public_key_pem": INSTANCE_KEY_PUB.export_key().decode('utf-8'), + "service_instance_public_key_pem": get_pem(INSTANCE_KEY_PUB).decode('utf-8'), "key_retention_mode": "LATEST_ONLY" }, } diff --git a/app/util.py b/app/util.py index b5b1ff1..f2b1be4 100644 --- a/app/util.py +++ b/app/util.py @@ -11,31 +11,51 @@ def load_file(filename: str) -> bytes: return content -def load_key(filename: str) -> "RsaKey": - try: - # Crypto | Cryptodome on Debian - from Crypto.PublicKey import RSA - from Crypto.PublicKey.RSA import RsaKey - except ModuleNotFoundError: - from Cryptodome.PublicKey import RSA - from Cryptodome.PublicKey.RSA import RsaKey +def load_private_key(filename: str) -> "RSAPrivateKey": + from cryptography.hazmat.primitives.serialization import load_pem_private_key log = logging.getLogger(__name__) log.debug(f'Importing RSA-Key from "{filename}"') - return RSA.import_key(extern_key=load_file(filename), passphrase=None) + + with open(filename, 'rb') as f: + data = f.read() + return load_pem_private_key(data.strip(), password=None) -def generate_key() -> "RsaKey": - try: - # Crypto | Cryptodome on Debian - from Crypto.PublicKey import RSA - from Crypto.PublicKey.RSA import RsaKey - except ModuleNotFoundError: - from Cryptodome.PublicKey import RSA - from Cryptodome.PublicKey.RSA import RsaKey +def load_public_key(filename: str) -> "RSAPublicKey": + from cryptography.hazmat.primitives.serialization import load_pem_public_key + + log = logging.getLogger(__name__) + log.debug(f'Importing RSA-Key from "{filename}"') + + with open(filename, 'rb') as f: + data = f.read() + return load_pem_public_key(data.strip()) + + +def get_pem(key) -> bytes | None: + from cryptography.hazmat.primitives.asymmetric.rsa import RSAPrivateKey, RSAPublicKey + from cryptography.hazmat.primitives import serialization + + if isinstance(key, RSAPrivateKey): + return key.private_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PrivateFormat.TraditionalOpenSSL, + encryption_algorithm=serialization.NoEncryption() + ) + if isinstance(key, RSAPublicKey): + return key.public_bytes( + encoding=serialization.Encoding.PEM, + format=serialization.PublicFormat.SubjectPublicKeyInfo + ) + + +def generate_private_key() -> "RSAPrivateKey": + from cryptography.hazmat.primitives.asymmetric import rsa + log = logging.getLogger(__name__) log.debug(f'Generating RSA-Key') - return RSA.generate(bits=2048) + return rsa.generate_private_key(public_exponent=65537, key_size=2048) class NV: diff --git a/requirements.txt b/requirements.txt index 6faecca..e13bedc 100644 --- a/requirements.txt +++ b/requirements.txt @@ -1,7 +1,7 @@ fastapi==0.115.8 uvicorn[standard]==0.34.0 -python-jose==3.4.0 -pycryptodome==3.21.0 +python-jose[cryptography]==3.4.0 +cryptography==44.0.2 python-dateutil==2.8.2 sqlalchemy==2.0.38 markdown==3.7 diff --git a/test/main.py b/test/main.py index a10f2c7..b63601e 100644 --- a/test/main.py +++ b/test/main.py @@ -16,7 +16,7 @@ sys.path.append('../') sys.path.append('../app') from app import main -from app.util import load_key +from util import load_private_key, load_public_key, get_pem client = TestClient(main.app) @@ -25,11 +25,11 @@ ORIGIN_REF, ALLOTMENT_REF, SECRET = str(uuid4()), '20000000-0000-0000-0000-00000 # INSTANCE_KEY_RSA = generate_key() # INSTANCE_KEY_PUB = INSTANCE_KEY_RSA.public_key() -INSTANCE_KEY_RSA = load_key(str(join(dirname(__file__), '../app/cert/instance.private.pem'))) -INSTANCE_KEY_PUB = load_key(str(join(dirname(__file__), '../app/cert/instance.public.pem'))) +INSTANCE_KEY_RSA = load_private_key(str(join(dirname(__file__), '../app/cert/instance.private.pem'))) +INSTANCE_KEY_PUB = load_public_key(str(join(dirname(__file__), '../app/cert/instance.public.pem'))) -jwt_encode_key = jwk.construct(INSTANCE_KEY_RSA.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) -jwt_decode_key = jwk.construct(INSTANCE_KEY_PUB.export_key().decode('utf-8'), algorithm=ALGORITHMS.RS256) +jwt_encode_key = jwk.construct(get_pem(INSTANCE_KEY_RSA), algorithm=ALGORITHMS.RS256) +jwt_decode_key = jwk.construct(get_pem(INSTANCE_KEY_PUB), algorithm=ALGORITHMS.RS256) def __bearer_token(origin_ref: str) -> str: