Files
crypto/bindings/python/luxcrypto/mlkem768.py
zeekay bbbd5fa1b3 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

89 lines
2.3 KiB
Python

"""ML-KEM-768 (FIPS 203) — key encapsulation via libluxcrypto."""
from __future__ import annotations
import ctypes
from luxcrypto._ffi import get_lib
# Sizes (queried from lib at first call, cached)
_PK_SIZE: int | None = None
_SK_SIZE: int | None = None
_CT_SIZE: int | None = None
_SS_SIZE = 32
def _sizes() -> tuple[int, int, int]:
global _PK_SIZE, _SK_SIZE, _CT_SIZE
if _PK_SIZE is None:
lib = get_lib()
_PK_SIZE = lib.mlkem768_pk_size()
_SK_SIZE = lib.mlkem768_sk_size()
_CT_SIZE = lib.mlkem768_ct_size()
return _PK_SIZE, _SK_SIZE, _CT_SIZE
def keypair() -> tuple[bytes, bytes]:
"""Generate an ML-KEM-768 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.mlkem768_keypair(pk_buf, ctypes.byref(pk_len), sk_buf, ctypes.byref(sk_len))
if rc != 0:
raise RuntimeError(f"mlkem768_keypair failed: {rc}")
return pk_buf.raw[: pk_len.value], sk_buf.raw[: sk_len.value]
def encapsulate(public_key: bytes) -> tuple[bytes, bytes]:
"""Encapsulate a shared secret for the given public key.
Returns (ciphertext, shared_secret).
"""
_, _, ct_size = _sizes()
lib = get_lib()
ct_buf = ctypes.create_string_buffer(ct_size)
ss_buf = ctypes.create_string_buffer(_SS_SIZE)
ct_len = ctypes.c_int(0)
ss_len = ctypes.c_int(0)
rc = lib.mlkem768_encapsulate(
public_key, len(public_key),
ct_buf, ctypes.byref(ct_len),
ss_buf, ctypes.byref(ss_len),
)
if rc != 0:
raise RuntimeError(f"mlkem768_encapsulate failed: {rc}")
return ct_buf.raw[: ct_len.value], ss_buf.raw[: ss_len.value]
def decapsulate(secret_key: bytes, ciphertext: bytes) -> bytes:
"""Decapsulate a shared secret from ciphertext.
Returns shared_secret.
"""
lib = get_lib()
ss_buf = ctypes.create_string_buffer(_SS_SIZE)
ss_len = ctypes.c_int(0)
rc = lib.mlkem768_decapsulate(
secret_key, len(secret_key),
ciphertext, len(ciphertext),
ss_buf, ctypes.byref(ss_len),
)
if rc != 0:
raise RuntimeError(f"mlkem768_decapsulate failed: {rc}")
return ss_buf.raw[: ss_len.value]