51 lines
1.8 KiB
Python
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,
|
|
)
|