Skip to content

Commit d552e3a

Browse files
committed
Version 0.14
Signed-off-by: Vlad Gheorghiu <[email protected]>
1 parent 099292d commit d552e3a

File tree

3 files changed

+79
-34
lines changed

3 files changed

+79
-34
lines changed

CHANGES.md

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,17 @@
1-
# Pre-release
1+
# Version 0.14.0 - August 9, 2025
22

33
- Added type checking and automatic linting/formatting, https://github.com/open-quantum-safe/liboqs-python/pull/97
44
- Added a utility function for de-structuring version strings in `oqs.py`
55
- `version(version_str: str) -> tuple[str, str, str]:` - Returns a tuple
66
containing the (major, minor, patch) versions
77
- A warning is issued only if the liboqs-python version's major and minor
88
numbers differ from those of liboqs, ignoring the patch version
9+
- Added stateful signature support via the `StatefulSignature` class
10+
- New enumeration helpers `get_enabled_stateful_sig_mechanisms()` and
11+
`get_supported_stateful_sig_mechanisms()`
12+
- ML-KEM keys can be generated from a seed via
13+
`KeyEncapsulation.generate_keypair_seed()`.
14+
- Minimum required Python 3 version bumped to 3.11
915

1016
# Version 0.12.0 - January 15, 2025
1117

RELEASE.md

Lines changed: 2 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,6 @@
11
# liboqs-python version 0.14.0
22

33
---
4-
# Added in version 0.14.0 July 2025
5-
6-
- Added stateful signature support via the `StatefulSignature` class.
7-
- New enumeration helpers `get_enabled_stateful_sig_mechanisms()` and
8-
`get_supported_stateful_sig_mechanisms()`.
9-
- Updated to liboqs 0.14.0.
10-
- ML-KEM keys can be generated from a seed via
11-
`KeyEncapsulation.generate_keypair_seed()`.
124

135
## About
146

@@ -32,13 +24,13 @@ See in particular limitations on intended use.
3224

3325
## Release notes
3426

35-
This release of liboqs-python was released on July 10, 2025. Its release
27+
This release of liboqs-python was released on August 9, 2025. Its release
3628
page on GitHub is
3729
https://github.com/open-quantum-safe/liboqs-python/releases/tag/0.14.0.
3830

3931
---
4032

4133
## What's New
4234

43-
This is the 11th release of liboqs-python. For a list of changes see
35+
This is the 12th release of liboqs-python. For a list of changes see
4436
[CHANGES.md](https://github.com/open-quantum-safe/liboqs-python/blob/main/CHANGES.md).

oqs/oqs.py

Lines changed: 70 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
cast,
3737
Optional,
3838
)
39+
3940
if TYPE_CHECKING:
4041
from collections.abc import Sequence, Iterable
4142
from types import TracebackType
@@ -244,7 +245,9 @@ def _load_liboqs() -> ct.CDLL:
244245
assert liboqs # noqa: S101
245246
except RuntimeError:
246247
# We don't have liboqs, so we try to install it automatically
247-
_install_liboqs(target_directory=oqs_install_dir, oqs_version_to_install=OQS_VERSION)
248+
_install_liboqs(
249+
target_directory=oqs_install_dir, oqs_version_to_install=OQS_VERSION
250+
)
248251
# Try loading it again
249252
try:
250253
liboqs = _load_shared_obj(
@@ -283,9 +286,13 @@ def oqs_version() -> str:
283286

284287
oqs_python_ver = oqs_python_version()
285288
if oqs_python_ver:
286-
oqs_python_ver_major, oqs_python_ver_minor, oqs_python_ver_patch = version(oqs_python_ver)
289+
oqs_python_ver_major, oqs_python_ver_minor, oqs_python_ver_patch = version(
290+
oqs_python_ver
291+
)
287292
# Warn the user if the liboqs version differs from liboqs-python version
288-
if not (oqs_ver_major == oqs_python_ver_major and oqs_ver_minor == oqs_python_ver_minor):
293+
if not (
294+
oqs_ver_major == oqs_python_ver_major and oqs_ver_minor == oqs_python_ver_minor
295+
):
289296
warnings.warn(
290297
f"liboqs version (major, minor) {oqs_version()} differs from liboqs-python version "
291298
f"{oqs_python_version()}",
@@ -296,7 +303,9 @@ def oqs_version() -> str:
296303
class MechanismNotSupportedError(Exception):
297304
"""Exception raised when an algorithm is not supported by OQS."""
298305

299-
def __init__(self, alg_name: str, supported: Optional[Iterable[str]] = None) -> None:
306+
def __init__(
307+
self, alg_name: str, supported: Optional[Iterable[str]] = None
308+
) -> None:
300309
"""
301310
Initialize the exception.
302311
@@ -363,7 +372,9 @@ class KeyEncapsulation(ct.Structure):
363372
("decaps_cb", ct.c_void_p),
364373
]
365374

366-
def __init__(self, alg_name: str, secret_key: Union[int, bytes, None] = None) -> None:
375+
def __init__(
376+
self, alg_name: str, secret_key: Union[int, bytes, None] = None
377+
) -> None:
367378
"""
368379
Create new KeyEncapsulation with the given algorithm.
369380
@@ -552,9 +563,13 @@ def is_kem_enabled(alg_name: str) -> bool:
552563
return native().OQS_KEM_alg_is_enabled(ct.create_string_buffer(alg_name.encode()))
553564

554565

555-
_KEM_alg_ids = [native().OQS_KEM_alg_identifier(i) for i in range(native().OQS_KEM_alg_count())]
556-
_supported_KEMs: tuple[str, ...] = tuple([i.decode() for i in _KEM_alg_ids]) # noqa: N816
557-
_enabled_KEMs: tuple[str, ...] = tuple([i for i in _supported_KEMs if is_kem_enabled(i)]) # noqa: N816
566+
_KEM_alg_ids = [
567+
native().OQS_KEM_alg_identifier(i) for i in range(native().OQS_KEM_alg_count())
568+
]
569+
_supported_KEMs: tuple[str, ...] = tuple([i.decode() for i in _KEM_alg_ids])
570+
_enabled_KEMs: tuple[str, ...] = tuple(
571+
[i for i in _supported_KEMs if is_kem_enabled(i)]
572+
)
558573

559574

560575
def get_enabled_kem_mechanisms() -> tuple[str, ...]:
@@ -603,7 +618,9 @@ class Signature(ct.Structure):
603618
("verify_with_ctx_cb", ct.c_void_p),
604619
]
605620

606-
def __init__(self, alg_name: str, secret_key: Union[int, bytes, None] = None) -> None:
621+
def __init__(
622+
self, alg_name: str, secret_key: Union[int, bytes, None] = None
623+
) -> None:
607624
"""
608625
Create new Signature with the given algorithm.
609626
@@ -746,8 +763,10 @@ def sign_with_ctx_str(self, message: bytes, context: bytes) -> bytes:
746763
:param message: the message to sign.
747764
"""
748765
if context and not self._sig.contents.sig_with_ctx_support:
749-
msg = (f"Signing with context is not supported for: "
750-
f"{self._sig.contents.method_name.decode()}")
766+
msg = (
767+
f"Signing with context is not supported for: "
768+
f"{self._sig.contents.method_name.decode()}"
769+
)
751770
raise RuntimeError(msg)
752771

753772
# Provide length to avoid extra null char
@@ -859,12 +878,18 @@ def sig_supports_context(alg_name: str) -> bool:
859878
860879
:param alg_name: A signature mechanism algorithm name.
861880
"""
862-
return bool(native().OQS_SIG_supports_ctx_str(ct.create_string_buffer(alg_name.encode())))
881+
return bool(
882+
native().OQS_SIG_supports_ctx_str(ct.create_string_buffer(alg_name.encode()))
883+
)
863884

864885

865-
_sig_alg_ids = [native().OQS_SIG_alg_identifier(i) for i in range(native().OQS_SIG_alg_count())]
886+
_sig_alg_ids = [
887+
native().OQS_SIG_alg_identifier(i) for i in range(native().OQS_SIG_alg_count())
888+
]
866889
_supported_sigs: tuple[str, ...] = tuple([i.decode() for i in _sig_alg_ids])
867-
_enabled_sigs: tuple[str, ...] = tuple([i for i in _supported_sigs if is_sig_enabled(i)])
890+
_enabled_sigs: tuple[str, ...] = tuple(
891+
[i for i in _supported_sigs if is_sig_enabled(i)]
892+
)
868893

869894

870895
def get_enabled_sig_mechanisms() -> tuple[str, ...]:
@@ -883,7 +908,9 @@ def get_supported_sig_mechanisms() -> tuple[str, ...]:
883908

884909
def is_stateful_sig_enabled(alg_name: str) -> bool:
885910
"""Check if a stateful signature algorithm is enabled."""
886-
return native().OQS_SIG_STFL_alg_is_enabled(ct.create_string_buffer(alg_name.encode()))
911+
return native().OQS_SIG_STFL_alg_is_enabled(
912+
ct.create_string_buffer(alg_name.encode())
913+
)
887914

888915

889916
_supported_stateful_sigs: tuple[str, ...] = tuple(
@@ -971,7 +998,9 @@ def __init__(self, alg_name: str, secret_key: Optional[bytes] = None) -> None:
971998
super().__init__()
972999

9731000
_check_alg(alg_name)
974-
self._sig = native().OQS_SIG_STFL_new(ct.create_string_buffer(alg_name.encode()))
1001+
self._sig = native().OQS_SIG_STFL_new(
1002+
ct.create_string_buffer(alg_name.encode())
1003+
)
9751004
if not self._sig:
9761005
msg = f"Could not allocate OQS_SIG_STFL for {alg_name}"
9771006
raise RuntimeError(msg)
@@ -1008,7 +1037,9 @@ def _cb(buf: bytes, length: int, _: ct.c_void_p) -> int:
10081037
return OQS_SUCCESS
10091038

10101039
self._store_cb = _cb # keep ref
1011-
native().OQS_SIG_STFL_SECRET_KEY_SET_store_cb(self._secret_key, self._store_cb, None)
1040+
native().OQS_SIG_STFL_SECRET_KEY_SET_store_cb(
1041+
self._secret_key, self._store_cb, None
1042+
)
10121043

10131044
def _new_secret_key(self) -> None:
10141045
"""Create a new secret key for the stateful signature."""
@@ -1022,7 +1053,9 @@ def _load_secret_key(self, data: bytes) -> None:
10221053
"""Load a secret key from bytes."""
10231054
self._new_secret_key()
10241055
buf = ct.create_string_buffer(data, len(data))
1025-
rc = native().OQS_SIG_STFL_SECRET_KEY_deserialize(self._secret_key, buf, len(data), None)
1056+
rc = native().OQS_SIG_STFL_SECRET_KEY_deserialize(
1057+
self._secret_key, buf, len(data), None
1058+
)
10261059
if rc != OQS_SUCCESS:
10271060
msg = "Secret‑key deserialization failed"
10281061
raise RuntimeError(msg)
@@ -1098,7 +1131,9 @@ def verify(self, message: bytes, signature: bytes, public_key: bytes) -> bool:
10981131
msg = ct.create_string_buffer(message, len(message))
10991132
sig = ct.create_string_buffer(signature, len(signature))
11001133
pk = ct.create_string_buffer(public_key, len(public_key))
1101-
rc = native().OQS_SIG_STFL_verify(self._sig, msg, len(message), sig, len(signature), pk)
1134+
rc = native().OQS_SIG_STFL_verify(
1135+
self._sig, msg, len(message), sig, len(signature), pk
1136+
)
11021137
return rc == OQS_SUCCESS
11031138

11041139
def export_secret_key(self) -> bytes:
@@ -1126,7 +1161,9 @@ def export_secret_key(self) -> bytes:
11261161
def sigs_total(self) -> int:
11271162
"""Get the total number of signatures that can be made with the secret key."""
11281163
total = ct.c_uint64()
1129-
rc = native().OQS_SIG_STFL_sigs_total(self._sig, ct.byref(total), self._secret_key)
1164+
rc = native().OQS_SIG_STFL_sigs_total(
1165+
self._sig, ct.byref(total), self._secret_key
1166+
)
11301167
if rc != OQS_SUCCESS:
11311168
msg = "Failed to get total signature count"
11321169
raise RuntimeError(msg)
@@ -1138,7 +1175,9 @@ def sigs_remaining(self) -> int:
11381175
msg = "Secret key not initialised – call generate_keypair() first"
11391176
raise ValueError(msg)
11401177
remain = ct.c_uint64()
1141-
rc = native().OQS_SIG_STFL_sigs_remaining(self._sig, ct.byref(remain), self._secret_key)
1178+
rc = native().OQS_SIG_STFL_sigs_remaining(
1179+
self._sig, ct.byref(remain), self._secret_key
1180+
)
11421181
if rc != OQS_SUCCESS:
11431182
msg = "Failed to get remaining signature count"
11441183
raise ValueError(msg)
@@ -1173,8 +1212,16 @@ def free(self) -> None:
11731212
native().OQS_SIG_STFL_new.restype = ct.POINTER(StatefulSignature)
11741213
native().OQS_SIG_STFL_SECRET_KEY_new.restype = ct.c_void_p
11751214
native().OQS_SIG_STFL_SECRET_KEY_new.argtypes = [ct.c_char_p]
1176-
native().OQS_SIG_STFL_SECRET_KEY_SET_store_cb.argtypes = [ct.c_void_p, ct.c_void_p, ct.c_void_p]
1177-
native().OQS_SIG_STFL_keypair.argtypes = [ct.POINTER(StatefulSignature), ct.c_void_p, ct.c_void_p]
1215+
native().OQS_SIG_STFL_SECRET_KEY_SET_store_cb.argtypes = [
1216+
ct.c_void_p,
1217+
ct.c_void_p,
1218+
ct.c_void_p,
1219+
]
1220+
native().OQS_SIG_STFL_keypair.argtypes = [
1221+
ct.POINTER(StatefulSignature),
1222+
ct.c_void_p,
1223+
ct.c_void_p,
1224+
]
11781225
native().OQS_SIG_STFL_sign.argtypes = [
11791226
ct.POINTER(StatefulSignature),
11801227
ct.c_void_p,

0 commit comments

Comments
 (0)