Files
paskia/paskia/util/crypto.py
T

51 lines
1.8 KiB
Python

import hashlib
import base64url
from cryptography.hazmat.primitives import serialization
from cryptography.hazmat.primitives.asymmetric.ed25519 import Ed25519PrivateKey
def hash_secret(*data: str | bytes, length=12) -> str:
"""A custom HMAC that securily combines and hashes the given data. The first argument should be a namespacing string."""
p = [d.encode() if hasattr(d, "encode") else d for d in data]
p += [len(x).to_bytes(8, "little") for x in [p, *p]]
return base64url.enc(hashlib.sha256(b"".join(p)).digest()[:length])
def secret_key() -> bytes:
"""Generate a new Ed25519 private key and return as 32 raw bytes."""
private_key = Ed25519PrivateKey.generate()
return private_key.private_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PrivateFormat.Raw,
encryption_algorithm=serialization.NoEncryption(),
)
def public_key_from_secret(secret_key_bytes: bytes) -> Ed25519PrivateKey:
"""Load Ed25519 private key from 32 raw bytes."""
return Ed25519PrivateKey.from_private_bytes(secret_key_bytes)
def get_public_key_der(private_key: Ed25519PrivateKey) -> bytes:
"""Get DER-encoded public key for kid generation."""
public_key = private_key.public_key()
return public_key.public_bytes(
encoding=serialization.Encoding.DER,
format=serialization.PublicFormat.SubjectPublicKeyInfo,
)
def generate_kid(public_key_der: bytes) -> str:
"""Generate key ID from public key DER bytes."""
return hashlib.sha256(public_key_der).hexdigest()[:16]
def get_public_key_raw(private_key: Ed25519PrivateKey) -> bytes:
"""Get raw 32-byte public key for JWKS."""
public_key = private_key.public_key()
return public_key.public_bytes(
encoding=serialization.Encoding.Raw,
format=serialization.PublicFormat.Raw,
)