This commit is contained in:
Cyber MacGeddon 2026-04-23 13:58:29 +01:00
parent 7be781b6e2
commit 1f639642ac

View file

@ -16,8 +16,8 @@ import os
import secrets
import uuid
from cryptography.hazmat.primitives import hashes, serialization
from cryptography.hazmat.primitives.asymmetric import padding, rsa
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric import ed25519
from trustgraph.schema import (
IamResponse, Error,
@ -39,7 +39,6 @@ API_KEY_RANDOM_BYTES = 24
JWT_ISSUER = "trustgraph-iam"
JWT_TTL_SECONDS = 3600
RSA_KEY_SIZE = 2048
def _now_iso():
@ -129,10 +128,11 @@ def _b64url(data):
def _generate_signing_keypair():
"""Return (kid, private_pem, public_pem) for a fresh RSA keypair."""
key = rsa.generate_private_key(
public_exponent=65537, key_size=RSA_KEY_SIZE,
)
"""Return (kid, private_pem, public_pem) for a fresh Ed25519
keypair. Ed25519 / EdDSA: small (32-byte public key), fast,
deterministic, side-channel-resistant by construction, free of
NIST-curve baggage."""
key = ed25519.Ed25519PrivateKey.generate()
private_pem = key.private_bytes(
encoding=serialization.Encoding.PEM,
format=serialization.PrivateFormat.PKCS8,
@ -147,8 +147,17 @@ def _generate_signing_keypair():
def _sign_jwt(kid, private_pem, claims):
"""Produce a compact-serialisation RS256 JWT for ``claims``."""
header = {"alg": "RS256", "typ": "JWT", "kid": kid}
"""Produce a compact-serialisation EdDSA (Ed25519) JWT for
``claims``."""
key = serialization.load_pem_private_key(
private_pem.encode("ascii"), password=None,
)
if not isinstance(key, ed25519.Ed25519PrivateKey):
raise RuntimeError(
f"signing key is not Ed25519: {type(key).__name__}"
)
header = {"alg": "EdDSA", "typ": "JWT", "kid": kid}
header_b = _b64url(json.dumps(
header, separators=(",", ":"), sort_keys=True,
).encode("utf-8"))
@ -156,11 +165,8 @@ def _sign_jwt(kid, private_pem, claims):
claims, separators=(",", ":"), sort_keys=True,
).encode("utf-8"))
signing_input = f"{header_b}.{payload_b}".encode("ascii")
signature = key.sign(signing_input)
key = serialization.load_pem_private_key(
private_pem.encode("ascii"), password=None,
)
signature = key.sign(signing_input, padding.PKCS1v15(), hashes.SHA256())
return f"{header_b}.{payload_b}.{_b64url(signature)}"