Files
zeekay bff34dd87a brand-neutral: drop LUX_ prefix from env/symbols/exports
Brand stays in import path only; symbols, env vars, and exports are
brand-neutral. One-release backwards-compat shim reads deprecated
LUX_ names with deprecation log.

Env: CRYPTO_BACKEND, CRYPTO_DIR, CRYPTO_BUILD_DIR, CRYPTO_LIB_DIR,
CRYPTO_LIB are canonical.

Rust: idiomatic Secp256k1Status enum; extern fn names dropped lux_ prefix.

TS/Python: cryptoAvailable / crypto_available; FFI symbol strings drop
lux_ prefix to match renamed C-ABI in luxcpp/crypto.

cabi/main.go: //export names drop lux_ prefix (mlkem768_*, mldsa65_*).
2025-12-27 20:22:38 -08:00

79 lines
1.9 KiB
Python

"""ML-DSA-65 (FIPS 204) — digital signatures via libluxcrypto."""
from __future__ import annotations
import ctypes
from luxcrypto._ffi import get_lib
_PK_SIZE: int | None = None
_SK_SIZE: int | None = None
_SIG_SIZE: int | None = None
def _sizes() -> tuple[int, int, int]:
global _PK_SIZE, _SK_SIZE, _SIG_SIZE
if _PK_SIZE is None:
lib = get_lib()
_PK_SIZE = lib.mldsa65_pk_size()
_SK_SIZE = lib.mldsa65_sk_size()
_SIG_SIZE = lib.mldsa65_sig_size()
return _PK_SIZE, _SK_SIZE, _SIG_SIZE
def keypair() -> tuple[bytes, bytes]:
"""Generate an ML-DSA-65 key pair.
Returns (public_key, secret_key).
"""
pk_size, sk_size, _ = _sizes()
lib = get_lib()
pk_buf = ctypes.create_string_buffer(pk_size)
sk_buf = ctypes.create_string_buffer(sk_size)
pk_len = ctypes.c_int(0)
sk_len = ctypes.c_int(0)
rc = lib.mldsa65_keypair(pk_buf, ctypes.byref(pk_len), sk_buf, ctypes.byref(sk_len))
if rc != 0:
raise RuntimeError(f"mldsa65_keypair failed: {rc}")
return pk_buf.raw[: pk_len.value], sk_buf.raw[: sk_len.value]
def sign(secret_key: bytes, message: bytes) -> bytes:
"""Sign a message with ML-DSA-65.
Returns signature bytes.
"""
_, _, sig_size = _sizes()
lib = get_lib()
sig_buf = ctypes.create_string_buffer(sig_size)
sig_len = ctypes.c_int(0)
rc = lib.mldsa65_sign(
secret_key, len(secret_key),
message, len(message),
sig_buf, ctypes.byref(sig_len),
)
if rc != 0:
raise RuntimeError(f"mldsa65_sign failed: {rc}")
return sig_buf.raw[: sig_len.value]
def verify(public_key: bytes, message: bytes, signature: bytes) -> bool:
"""Verify an ML-DSA-65 signature.
Returns True if valid, False otherwise.
"""
lib = get_lib()
rc = lib.mldsa65_verify(
public_key, len(public_key),
message, len(message),
signature, len(signature),
)
return rc == 0